All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gurux.dlms.GXDLMSNotify Maven / Gradle / Ivy

There is a newer version: 4.0.72
Show newest version
//
// --------------------------------------------------------------------------
//  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]);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy