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

org.openmuc.jdlms.transportlayer.client.Iec21Layer Maven / Gradle / Ivy

Go to download

jDLMS is a library implementing the DLMS/COSEM (IEC 62056) communication standard.

There is a newer version: 1.8.0
Show newest version
/*
 * Copyright 2012-15 Fraunhofer ISE
 *
 * This file is part of jDLMS.
 * For more information visit http://www.openmuc.org
 *
 * jDLMS 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 3 of the License, or
 * (at your option) any later version.
 *
 * jDLMS 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 jDLMS.  If not, see .
 *
 */
package org.openmuc.jdlms.transportlayer.client;

import static org.openmuc.jdlms.JDlmsException.Fault.SYSTEM;
import static org.openmuc.jdlms.JDlmsException.Fault.USER;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.text.MessageFormat;

import org.openmuc.jdlms.FatalJDlmsException;
import org.openmuc.jdlms.HexConverter;
import org.openmuc.jdlms.JDlmsException.ExceptionId;
import org.openmuc.jdlms.settings.client.SerialSettings;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.CommPortTimeoutException;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import gnu.io.serialport.DataBits;
import gnu.io.serialport.Parity;
import gnu.io.serialport.StopBits;

/**
 * This class represents a connection on the physical layer according to IEC 62056-21 in protocol mode E.
 */
public class Iec21Layer implements TransportLayer {
    private static final String APP_NAME = "org.openmuc.jdlms.SERIAL";

    private static final int CR = 0x0D;
    private static final int LF = 0x0A;

    /**
     * {@code / ? }
     */
    private static final byte[] REQUEST_MSG_1 = new byte[] { 0x2F, 0x3F };
    /**
     * {@code ! CR LF}
     */
    private static final byte[] REQUEST_MSG_3 = new byte[] { 0x21, CR, LF };

    /**
     * {@code ACK 2 0 2 CR LF}
     * 

* ACK [HDLC protocol procedure] [initial bd 300] [binary mode] *

*/ private static final byte[] ACKNOWLEDGE = new byte[] { 0x06, 0x32, 0x30, 0x32, CR, LF }; private SerialPort serialPort; private final SerialSettings settings; private boolean closed; public Iec21Layer(SerialSettings settings) { this.settings = settings; this.closed = true; } @Override public void setTimeout(int timeout) throws IOException { try { this.serialPort.setCommPortTimeout(timeout); } catch (UnsupportedCommOperationException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_INCOMPATIBLE_TO_OS, SYSTEM, "RXTX is not compatible to your OS.", e); } } @Override public InputStream getInputStream() throws IOException { return this.serialPort.inputStream(); } @Override public OutputStream getOutpuStream() throws IOException { return this.serialPort.outputStream(); } @Override public boolean isClosed() { return this.closed; } @Override public void open() throws IOException { if (isClosed()) { try { openPhysicalConnection(); if (settings.iec21Handshake() == DataFlowControl.ENABLED) { connectWithHandshake(); } else { connectWithoutHandshake(); } } catch (IOException e) { if (serialPort != null) { serialPort.close(); } throw e; } this.closed = false; } } private void connectWithHandshake() throws IOException { try { serialPort.setCommPortTimeout(2000); } catch (UnsupportedCommOperationException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_INCOMPATIBLE_TO_OS, SYSTEM, "RXTX is not compatible to your OS.", e); } byte[] iec21AddressBytes = settings.iec21Address().trim().getBytes(); byte[] requestMsg = ByteBuffer.allocate(REQUEST_MSG_1.length + iec21AddressBytes.length + REQUEST_MSG_3.length) .put(REQUEST_MSG_1) .put(iec21AddressBytes) .put(REQUEST_MSG_3) .array(); write(requestMsg); char baudRateSetting; try { baudRateSetting = listenForIdentificationMessage(); } catch (CommPortTimeoutException e) { throw new FatalJDlmsException(ExceptionId.IEC_21_CONNECTION_ESTABLISH_ERROR, USER, "Timed out, while waiting for the HDLC identification message.", e); } catch (FatalJDlmsException e) { throw e; } catch (IOException e) { throw new FatalJDlmsException(ExceptionId.IEC_21_CONNECTION_ESTABLISH_ERROR, SYSTEM, MessageFormat .format("{0} Sended requestmessage: {1}", e.getMessage(), HexConverter.toHexString(requestMsg)), e); } int baudRate = baudRateFor(baudRateSetting); ACKNOWLEDGE[2] = (byte) baudRateSetting; write(ACKNOWLEDGE); // Sleep for about 250 milliseconds to make sure, that the // acknowledge message has been completely transmitted prior // to changing the baud rate try { Thread.sleep(settings.baudrateChangeDelay()); } catch (InterruptedException e) { } // change mode to Z baud, 7,1,E try { serialPort.setSerialPortParams(baudRate, DataBits.DATABITS_7, StopBits.STOPBITS_1, Parity.EVEN); } catch (UnsupportedCommOperationException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_INCOMPATIBLE_TO_OS, SYSTEM, MessageFormat.format("Serial Port does not support {0} bd 7E1", baudRate), e); } listenForAck(); // change mode to Z baud, 8,1,N try { serialPort.setSerialPortParams(baudRate, DataBits.DATABITS_8, StopBits.STOPBITS_1, Parity.NONE); } catch (UnsupportedCommOperationException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_INCOMPATIBLE_TO_OS, SYSTEM, MessageFormat.format("Serial Port does not support {0}bd 8N1", baudRate)); } } private void connectWithoutHandshake() throws IOException { int maxBaudrate = settings.baudrate(); try { serialPort.setSerialPortParams(maxBaudrate, DataBits.DATABITS_8, StopBits.STOPBITS_1, Parity.NONE); } catch (UnsupportedCommOperationException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_INCOMPATIBLE_TO_OS, SYSTEM, MessageFormat.format("Serial Port does not support {0}bd 8N1", maxBaudrate), e); } } public synchronized void send(byte[] data) throws IOException { if (isClosed()) { throw new FatalJDlmsException(ExceptionId.CONNECTION_CLOSED, USER, "Cannot send data. DLMS Client not connected. Open the connection to communicate."); } write(data); } private void write(byte[] data) throws IOException { getOutpuStream().write(data); getOutpuStream().flush(); } @Override public void close() { serialPort.close(); closed = true; } private int baudRateFor(char baudCharacter) throws IOException { // Encoded baud rate (see IEC 62056-21 6.3.14 13c). switch (baudCharacter) { case '0': return 300; case '1': return 600; case '2': return 1200; case '3': return 2400; case '4': return 4800; case '5': return 9600; case '6': return 19200; default: throw new FatalJDlmsException(ExceptionId.IEC_21_CONNECTION_ESTABLISH_ERROR, SYSTEM, String.format( "Syntax error in identification message received: unknown baud rate received. Baud character was 0x%02X. or char '%s'", (byte) baudCharacter, String.valueOf(baudCharacter))); } } private void openPhysicalConnection() throws FatalJDlmsException { if (serialPort == null) { try { serialPort = acquireSerialPort(settings.serialPortName()); } catch (FatalJDlmsException e) { if (serialPort != null) { serialPort.close(); } throw e; } } } private void listenForAck() throws IOException { byte[] ackMsg = new byte[ACKNOWLEDGE.length]; try { int ackLength = getInputStream().read(ackMsg); if (ackLength != ackMsg.length) { throw new FatalJDlmsException(ExceptionId.IEC_21_UNKNOWN_ACK_MSG, SYSTEM, "Either the remote meter implements a newer protocol, or you may clear your serial port and try again."); } } catch (CommPortTimeoutException e) { throw new FatalJDlmsException(ExceptionId.IEC_21_WRONG_BAUD_RATE_CHANGE_DELAY, USER, "The baud rate change delay was wrong. Try an onther one."); } } private SerialPort acquireSerialPort(String serialPortName) throws FatalJDlmsException { CommPortIdentifier portIdentifier; try { portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName); } catch (NoSuchPortException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_NO_SUCH_PORT, USER, "The specified port does not exist.", e); } CommPort commPort; try { commPort = portIdentifier.open(APP_NAME, 2000); } catch (PortInUseException e) { throw new FatalJDlmsException(ExceptionId.JRXTX_PORT_IN_USE, USER, "The specified port is already in use.", e); } if (!(commPort instanceof SerialPort)) { // may never be the case commPort.close(); throw new FatalJDlmsException(ExceptionId.JRXTX_PORT_NOT_SERIAL, USER, "The specified CommPort is not a serial port"); } try { SerialPort serialPort = (SerialPort) commPort; serialPort.setSerialPortParams(300, DataBits.DATABITS_7, StopBits.STOPBITS_1, Parity.EVEN); return serialPort; } catch (UnsupportedCommOperationException e) { if (commPort != null) { commPort.close(); } throw new FatalJDlmsException(ExceptionId.JRXTX_INCOMPATIBLE_TO_OS, SYSTEM, "Unable to set the baud rate or other serial port parameters.", e); } } private char listenForIdentificationMessage() throws IOException { try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) { int b; while ((b = getInputStream().read()) != CR) { byteStream.write(b); } // read CR LF byteStream.write(b); b = getInputStream().read(); byteStream.write(b); byte[] response = byteStream.toByteArray(); return (char) response[4]; } } public enum DataFlowControl { ENABLED, DISABLED } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy