gurux.dlms.GXDLMSNotify Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gurux.dlms Show documentation
Show all versions of gurux.dlms Show documentation
gurux.dlms.java package is used to communicate with DLMS devices.
//
// --------------------------------------------------------------------------
// Gurux Ltd
//
//
//
// Filename: $HeadURL$
//
// Version: $Revision$,
// $Date$
// $Author$
//
// Copyright (c) Gurux Ltd
//
//---------------------------------------------------------------------------
//
// DESCRIPTION
//
// This file is a part of Gurux Device Framework.
//
// Gurux Device Framework is Open Source 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; version 2 of the License.
// Gurux Device Framework 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.
//
// More information of Gurux products: https://www.gurux.org
//
// This code is licensed under the GNU General Public License v2.
// Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
//---------------------------------------------------------------------------
package gurux.dlms;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import gurux.dlms.enums.Command;
import gurux.dlms.enums.Conformance;
import gurux.dlms.enums.DataType;
import gurux.dlms.enums.InterfaceType;
import gurux.dlms.enums.ObjectType;
import gurux.dlms.enums.Priority;
import gurux.dlms.enums.ServiceClass;
import gurux.dlms.internal.GXCommon;
import gurux.dlms.objects.GXDLMSCaptureObject;
import gurux.dlms.objects.GXDLMSObject;
import gurux.dlms.objects.GXDLMSObjectCollection;
import gurux.dlms.objects.GXDLMSPushSetup;
/**
* This class is used to send data notify and push messages to the clients.
*
* @author Gurux Ltd.
*/
public class GXDLMSNotify {
/**
* DLMS settings.
*/
private final GXDLMSSettings settings;
/**
* Constructor.
*
* @param useLogicalNameReferencing
* Is Logical Name referencing used.
* @param clientAddress
* Server address.
* @param serverAddress
* Client address.
* @param interfaceType
* Object type.
*/
public GXDLMSNotify(final boolean useLogicalNameReferencing, final int clientAddress, final int serverAddress,
final InterfaceType interfaceType) {
IGXCryptoNotifier notifier1 = this instanceof IGXCryptoNotifier ? (IGXCryptoNotifier) this : null;
IGXCustomObjectNotifier notifier2 =
this instanceof IGXCustomObjectNotifier ? (IGXCustomObjectNotifier) this : null;
settings = new GXDLMSSettings(true, notifier1, notifier2);
setUseLogicalNameReferencing(useLogicalNameReferencing);
settings.setClientAddress(clientAddress);
settings.setServerAddress(serverAddress);
settings.setInterfaceType(interfaceType);
}
/**
* What kind of services are used.
*
* @return Functionality.
*/
public final java.util.Set getConformance() {
return settings.getNegotiatedConformance();
}
/**
* @param value
* What kind of services are used.
*/
public final void setConformance(final java.util.Set value) {
settings.setNegotiatedConformance(value);
}
/**
* @param value
* Cipher interface that is used to cipher PDU.
*/
protected final void setCipher(final GXICipher value) {
settings.setCipher(value);
}
/**
* @return Get list of meter's objects.
*/
public final GXDLMSObjectCollection getObjects() {
return settings.getObjects();
}
/**
* @return HDLC connection settings.
* @deprecated use {@link getHdlcSettings} instead.
*/
public final GXDLMSLimits getLimits() {
return (GXDLMSLimits) settings.getHdlcSettings();
}
/**
* @return HDLC connection settings.
*/
public final GXHdlcSettings getHdlcSettings() {
return settings.getHdlcSettings();
}
/**
* Retrieves the maximum size of received PDU. PDU size tells maximum size
* of PDU packet. Value can be from 0 to 0xFFFF. By default the value is
* 0xFFFF.
*
* @see GXDLMSClient#getClientAddress
* @see GXDLMSClient#getServerAddress
* @see GXDLMSClient#getUseLogicalNameReferencing
* @return Maximum size of received PDU.
*/
public final int getMaxReceivePDUSize() {
return settings.getMaxPduSize();
}
/**
* @param value
* Maximum size of received PDU.
*/
public final void setMaxReceivePDUSize(final int value) {
settings.setMaxPduSize(value);
}
/**
* Determines, whether Logical, or Short name, referencing is used.
* Referencing depends on the device to communicate with. Normally, a device
* supports only either Logical or Short name referencing. The referencing
* is defined by the device manufacturer. If the referencing is wrong, the
* SNMR message will fail.
*
* @return Is Logical Name referencing used.
*/
public final boolean getUseLogicalNameReferencing() {
return settings.getUseLogicalNameReferencing();
}
/**
* @param value
* Is Logical Name referencing used.
*/
public final void setUseLogicalNameReferencing(final boolean value) {
settings.setUseLogicalNameReferencing(value);
}
/**
* @return Used Priority.
*/
public final Priority getPriority() {
return settings.getPriority();
}
/**
* @param value
* Used Priority.
*/
public final void setPriority(final Priority value) {
settings.setPriority(value);
}
/**
* @return Used service class.
*/
public final ServiceClass getServiceClass() {
return settings.getServiceClass();
}
/**
* @param value
* Used service class.
*/
public final void setServiceClass(final ServiceClass value) {
settings.setServiceClass(value);
}
/**
* @return Invoke ID.
*/
public final int getInvokeID() {
return settings.getInvokeID();
}
/**
* @param value
* Invoke ID.
*/
public final void setInvokeID(final byte value) {
settings.setInvokeID(value);
}
/**
* Removes the HDLC frame from the packet, and returns COSEM data only.
*
* @param reply
* The received data from the device.
* @param data
* Information from the received data.
* @return Is frame complete.
* @throws NoSuchPaddingException
* No such padding exception.
* @throws NoSuchAlgorithmException
* No such algorithm exception.
* @throws InvalidAlgorithmParameterException
* Invalid algorithm parameter exception.
* @throws InvalidKeyException
* Invalid key exception.
* @throws BadPaddingException
* Bad padding exception.
* @throws IllegalBlockSizeException
* Illegal block size exception.
* @throws SignatureException
* Signature exception.
*/
public final boolean getData(final GXByteBuffer reply, final GXReplyData data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, SignatureException {
return GXDLMS.getData(settings, reply, data, null);
}
/**
* Add value of COSEM object to byte buffer. AddData method can be used with
* GetDataNotificationMessage -method. DLMS specification do not specify the
* structure of Data-Notification body. So each manufacture can sent
* different data.
*
* @param obj
* COSEM object.
* @param index
* Attribute index.
* @param buff
* Byte buffer.
*/
public final void addData(final GXDLMSObject obj, final int index, final GXByteBuffer buff) {
DataType dt;
ValueEventArgs e = new ValueEventArgs(settings, obj, index, 0, null);
Object value = obj.getValue(settings, e);
dt = obj.getDataType(index);
if (dt == DataType.NONE && value != null) {
dt = GXDLMSConverter.getDLMSDataType(value);
}
GXCommon.setData(settings, buff, dt, value);
}
/**
* Add value of COSEM object to byte buffer. AddData method can be used with
* GetDataNotificationMessage -method. DLMS specification do not specify the
* structure of Data-Notification body. So each manufacture can sent
* different data.
*
* @param value
* Added value.
* @param type
* Value data type.
* @param buff
* Byte buffer.
*/
public final void addData(final Object value, final DataType type, final GXByteBuffer buff) {
GXCommon.setData(settings, buff, type, value);
}
/**
* Generates data notification message.
*
* @param time
* Date time. Set to null or Date(0) if not used
* @param data
* Notification body.
* @return Generated data notification message(s).
* @throws NoSuchPaddingException
* No such padding exception.
* @throws NoSuchAlgorithmException
* No such algorithm exception.
* @throws InvalidAlgorithmParameterException
* Invalid algorithm parameter exception.
* @throws InvalidKeyException
* Invalid key exception.
* @throws BadPaddingException
* Bad padding exception.
* @throws IllegalBlockSizeException
* Illegal block size exception.
* @throws SignatureException
* Signature exception.
*/
public final byte[][] generateDataNotificationMessages(final Date time, final byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, SignatureException {
return generateDataNotificationMessages(time, new GXByteBuffer(data));
}
/**
* Generates data notification message.
*
* @param time
* Date time. Set Date(0) if not added.
* @param data
* Notification body.
* @return Generated data notification message(s).
* @throws NoSuchPaddingException
* No such padding exception.
* @throws NoSuchAlgorithmException
* No such algorithm exception.
* @throws InvalidAlgorithmParameterException
* Invalid algorithm parameter exception.
* @throws InvalidKeyException
* Invalid key exception.
* @throws BadPaddingException
* Bad padding exception.
* @throws IllegalBlockSizeException
* Illegal block size exception.
* @throws SignatureException
* Signature exception.
*/
public final byte[][] generateDataNotificationMessages(final Date time, final GXByteBuffer data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, SignatureException {
List reply;
if (getUseLogicalNameReferencing()) {
GXDLMSLNParameters p =
new GXDLMSLNParameters(settings, 0, Command.DATA_NOTIFICATION, 0, null, data, 0xff, Command.NONE);
if (time == null) {
p.setTime(null);
} else {
p.setTime(new GXDateTime(time));
}
reply = GXDLMS.getLnMessages(p);
} else {
GXDLMSSNParameters p = new GXDLMSSNParameters(settings, Command.DATA_NOTIFICATION, 1, 0, data, null);
reply = GXDLMS.getSnMessages(p);
}
if (!settings.getNegotiatedConformance().contains(Conformance.GENERAL_BLOCK_TRANSFER) && reply.size() != 1) {
throw new IllegalArgumentException("Data is not fit to one PDU. Use general block transfer.");
}
return reply.toArray(new byte[0][0]);
}
/**
* Generates data notification message.
*
* @param date
* Date time. Set To null if not added.
* @param objects
* List of objects and attribute indexes to notify.
* @return Generated data notification message(s).
* @throws NoSuchPaddingException
* No such padding exception.
* @throws NoSuchAlgorithmException
* No such algorithm exception.
* @throws InvalidAlgorithmParameterException
* Invalid algorithm parameter exception.
* @throws InvalidKeyException
* Invalid key exception.
* @throws BadPaddingException
* Bad padding exception.
* @throws IllegalBlockSizeException
* Illegal block size exception.
* @throws SignatureException
* Signature exception.
*/
public final byte[][] generateDataNotificationMessages(final Date date,
final List> objects)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, SignatureException {
if (objects == null) {
throw new IllegalArgumentException("objects");
}
GXByteBuffer buff = new GXByteBuffer();
buff.setUInt8((byte) DataType.STRUCTURE.getValue());
GXCommon.setObjectCount(objects.size(), buff);
for (Entry it : objects) {
addData(it.getKey(), it.getValue(), buff);
}
return generateDataNotificationMessages(date, buff);
}
/**
* Generates push setup message.
*
* @param date
* Date time. Set to null or Date(0) if not used.
* @param push
* Target Push object.
* @return Generated data notification message(s).
* @throws NoSuchPaddingException
* No such padding exception.
* @throws NoSuchAlgorithmException
* No such algorithm exception.
* @throws InvalidAlgorithmParameterException
* Invalid algorithm parameter exception.
* @throws InvalidKeyException
* Invalid key exception.
* @throws BadPaddingException
* Bad padding exception.
* @throws IllegalBlockSizeException
* Illegal block size exception.
* @throws SignatureException
* Signature exception.
*/
public final byte[][] generatePushSetupMessages(final Date date, final GXDLMSPushSetup push)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, SignatureException {
if (push == null) {
throw new IllegalArgumentException("push");
}
GXByteBuffer buff = new GXByteBuffer();
buff.setUInt8((byte) DataType.STRUCTURE.getValue());
GXCommon.setObjectCount(push.getPushObjectList().size(), buff);
for (Entry it : push.getPushObjectList()) {
addData(it.getKey(), it.getValue().getAttributeIndex(), buff);
}
return generateDataNotificationMessages(date, buff);
}
/**
* Returns collection of push objects. If this method is used Push object
* must be set for first object on push object list.
*
* @param data
* Received value.
* @return Array of objects and called indexes.
*/
public final List> parsePush(final List> data) {
GXDLMSObject obj;
int index;
DataType dt;
Object value;
List> items = new ArrayList>();
if (data != null) {
GXDLMSConverter c = new GXDLMSConverter();
for (Object it : (List>) data.get(0)) {
List> tmp = (List>) it;
int classID = ((Number) (tmp.get(0))).intValue() & 0xFFFF;
if (classID > 0) {
GXDLMSObject comp;
comp = getObjects().findByLN(ObjectType.forValue(classID),
GXCommon.toLogicalName((byte[]) tmp.get(1)));
if (comp == null) {
comp = GXDLMSClient.createDLMSObject(settings, classID, 0, 0, tmp.get(1), null, 2);
settings.getObjects().add(comp);
c.updateOBISCodeInformation(comp);
}
if (comp.getClass() != GXDLMSObject.class) {
items.add(new GXSimpleEntry(comp, ((Number) tmp.get(2)).intValue()));
} else {
Logger.getLogger(GXDLMS.class.getName()).log(Level.INFO, "Unknown object: "
+ String.valueOf(classID) + " " + GXCommon.toLogicalName((byte[]) tmp.get(1)));
}
}
}
boolean useUtc;
if (settings != null) {
useUtc = settings.getUseUtc2NormalTime();
} else {
useUtc = false;
}
for (int pos = 0; pos < data.size(); ++pos) {
obj = items.get(pos).getKey();
value = data.get(pos);
index = items.get(pos).getValue();
if (value instanceof byte[]) {
dt = obj.getUIDataType(index);
if (dt != DataType.NONE) {
value = GXDLMSClient.changeType((byte[]) value, dt, useUtc);
}
}
ValueEventArgs e = new ValueEventArgs(settings, obj, index, 0, null);
e.setValue(value);
obj.setValue(settings, e);
e.setValue(value);
e = new ValueEventArgs(settings, items.get(pos).getKey(), items.get(pos).getValue(), 0, null);
e.setValue(data.get(pos));
items.get(pos).getKey().setValue(settings, e);
}
}
return items;
}
/**
* Returns collection of push objects.
*
* @param objects
* Array of objects and called indexes.
* @param data
* Received data.
*/
public final void parsePush(final List> objects, final List> data) {
GXDLMSObject obj;
int index;
DataType dt;
Object value;
if (data == null) {
throw new IllegalArgumentException("Invalid push message.");
}
if (data.size() != objects.size()) {
throw new IllegalArgumentException("Push arguments do not match.");
}
for (int pos = 0; pos < data.size(); ++pos) {
obj = objects.get(pos).getKey();
value = data.get(pos);
index = objects.get(pos).getValue();
if (value instanceof byte[]) {
dt = obj.getUIDataType(index);
if (dt != DataType.NONE) {
value = GXDLMSClient.changeType((byte[]) value, dt, settings.getUseUtc2NormalTime());
}
}
ValueEventArgs e = new ValueEventArgs(settings, obj, index, 0, null);
e.setValue(value);
obj.setValue(settings, e);
e.setValue(value);
}
}
/**
* Sends Event Notification or Information Report Request.
*
* @param time
* Send time.
* @param list
* List of COSEM object and attribute index to report.
* @return Report request as byte array.
* @throws NoSuchPaddingException
* No such padding exception.
* @throws NoSuchAlgorithmException
* No such algorithm exception.
* @throws InvalidAlgorithmParameterException
* Invalid algorithm parameter exception.
* @throws InvalidKeyException
* Invalid key exception.
* @throws BadPaddingException
* Bad padding exception.
* @throws IllegalBlockSizeException
* Illegal block size exception.
* @throws SignatureException
* Signature exception.
*/
public byte[][] generateReport(final GXDateTime time, final List> list)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, SignatureException {
if (list == null || list.size() == 0) {
throw new IllegalArgumentException("list");
}
if (getUseLogicalNameReferencing() && list.size() != 1) {
throw new IllegalArgumentException("Only one object can send with Event Notification request.");
}
GXByteBuffer buff = new GXByteBuffer();
List reply;
if (getUseLogicalNameReferencing()) {
for (Entry it : list) {
buff.setUInt16(it.getKey().getObjectType().getValue());
buff.set(GXCommon.logicalNameToBytes(it.getKey().getLogicalName()));
buff.setUInt8(it.getValue());
addData(it.getKey(), it.getValue(), buff);
}
GXDLMSLNParameters p =
new GXDLMSLNParameters(settings, 0, Command.EVENT_NOTIFICATION, 0, null, buff, 0xff, Command.NONE);
p.setTime(time);
reply = GXDLMS.getLnMessages(p);
} else {
GXDLMSSNParameters p =
new GXDLMSSNParameters(settings, Command.INFORMATION_REPORT, list.size(), 0xFF, null, buff);
for (Entry it : list) {
// Add variable type.
buff.setUInt8(VariableAccessSpecification.VARIABLE_NAME);
int sn = it.getKey().getShortName();
sn += (it.getValue() - 1) * 8;
buff.setUInt16(sn);
}
GXCommon.setObjectCount(list.size(), buff);
for (Entry it : list) {
addData(it.getKey(), it.getValue(), buff);
}
reply = GXDLMS.getSnMessages(p);
}
return reply.toArray(new byte[0][0]);
}
}