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

de.ibapl.spsw.logging.LoggingSerialPortSocket Maven / Gradle / Ivy

The newest version!
/*
 * SPSW - Drivers for the serial port, https://github.com/aploese/spsw/
 * Copyright (C) 2009-2021, Arne Plöse and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package de.ibapl.spsw.logging;

import de.ibapl.spsw.api.DataBits;
import de.ibapl.spsw.api.FlowControl;
import de.ibapl.spsw.api.Parity;
import de.ibapl.spsw.api.SerialPortSocket;
import de.ibapl.spsw.api.Speed;
import de.ibapl.spsw.api.StopBits;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Set;
import org.osgi.annotation.versioning.ProviderType;

/**
 * A wrapper around an {@link SerialPortSocket} to log its activity. Use the
 * constructor methods
 * {@link #wrapWithAsciiOutputStream(SerialPortSocket, OutputStream, boolean, TimeStampLogging)}
 * and
 * {@link #wrapWithHexOutputStream(SerialPortSocket, OutputStream, boolean, TimeStampLogging)}
 * to construct a new instance.
 *
 * @author Arne Plöse
 */
@ProviderType
public class LoggingSerialPortSocket implements SerialPortSocket, LogExplainRead, LogExplainWrite {

    private static final Cleaner cleaner = Cleaner.create();

    static class LogWriterCloser implements Runnable {

        final LogWriter logWriter;

        LogWriterCloser(final LogWriter logWriter) {
            this.logWriter = logWriter;
        }

        public void run() {
            try {
                if (logWriter != null) {
                    logWriter.close();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            }

        }
    }

    private class LIS extends InputStream {

        private final InputStream is;

        LIS(InputStream is) {
            this.is = is;
        }

        @Override
        public int available() throws IOException {
            logWriter.beforeAvailable(Instant.now());
            try {
                final int result = is.available();
                logWriter.afterAvailable(Instant.now(), result);
                return result;
            } catch (IOException e) {
                logWriter.afterAvailable(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public void close() throws IOException {
            logWriter.beforeIsClose(Instant.now());
            try {
                is.close();
                logWriter.afterIsClose(Instant.now());
            } catch (IOException e) {
                logWriter.afterIsClose(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public int read() throws IOException {
            logWriter.beforeIsRead(Instant.now());
            try {
                final int result = is.read();
                logWriter.afterIsRead(Instant.now(), result);
                return result;
            } catch (IOException e) {
                logWriter.afterIsRead(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public int read(byte b[]) throws IOException {
            logWriter.beforeIsRead(Instant.now());
            try {
                final int result = is.read(b);
                logWriter.afterIsRead(Instant.now(), result, b, 0);
                return result;
            } catch (IOException e) {
                logWriter.afterIsRead(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public int read(byte b[], int off, int len) throws IOException {
            logWriter.beforeIsRead(Instant.now());
            try {
                final int result = is.read(b, off, len);
                logWriter.afterIsRead(Instant.now(), result, b, off);
                return result;
            } catch (IOException e) {
                logWriter.afterIsRead(Instant.now(), e);
                throw e;
            }

        }

    }

    @Override
    public int read(ByteBuffer dst) throws IOException {
        final int position = dst.position();
        logWriter.beforeChannelRead(Instant.now());
        try {
            final int result = serialPortSocket.read(dst);
            logWriter.afterChannelRead(Instant.now(), dst, position);
            return result;
        } catch (IOException e) {
            logWriter.afterChannelRead(Instant.now(), dst, e);
            throw e;
        }
    }

    @Override
    public int write(ByteBuffer src) throws IOException {
        logWriter.beforeChannelWrite(Instant.now(), src);
        try {
            final int result = serialPortSocket.write(src);
            logWriter.afterChannelWrite(Instant.now());
            return result;
        } catch (IOException e) {
            logWriter.afterChannelWrite(Instant.now(), src, e);
            throw e;
        }
    }

    private class LOS extends OutputStream {

        private final OutputStream os;

        LOS(OutputStream os) {
            this.os = os;
        }

        @Override
        public void close() throws IOException {
            logWriter.beforeOsClose(Instant.now());
            try {
                os.close();
                logWriter.afterOsClose(Instant.now());
            } catch (IOException e) {
                logWriter.afterOsClose(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public void flush() throws IOException {
            logWriter.beforeFlush(Instant.now());
            try {
                os.flush();
                logWriter.afterFlush(Instant.now());
            } catch (IOException e) {
                logWriter.afterFlush(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public void write(byte b[]) throws IOException {
            logWriter.beforeOsWrite(Instant.now(), b);
            try {
                os.write(b);
                logWriter.afterOsWrite(Instant.now());
            } catch (IOException e) {
                logWriter.afterOsWrite(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public void write(byte b[], int off, int len) throws IOException {
            logWriter.beforeOsWrite(Instant.now(), b, off, len);
            try {
                os.write(b, off, len);
                logWriter.afterOsWrite(Instant.now());
            } catch (IOException e) {
                logWriter.afterOsWrite(Instant.now(), e);
                throw e;
            }
        }

        @Override
        public void write(int b) throws IOException {
            logWriter.beforeOsWrite(Instant.now(), (byte) b);
            try {
                os.write(b);
                logWriter.afterOsWrite(Instant.now());
            } catch (IOException e) {
                logWriter.afterOsWrite(Instant.now(), e);
                throw e;
            }
        }

    }

    /**
     * Format all transfered bytes in their ASCII representation. The value of
     * 83 will be printed as letter S.
     *
     * @param serialPortSocket
     * @param logOs
     * @param verbose
     * @param timeStampLogging
     * @return the new instance with ASCII logging.
     * @throws FileNotFoundException
     */
    public static LoggingSerialPortSocket wrapWithAsciiOutputStream(SerialPortSocket serialPortSocket,
            OutputStream logOs, boolean verbose, TimeStampLogging timeStampLogging) throws FileNotFoundException, IOException {
        return new LoggingSerialPortSocket(serialPortSocket, logOs, true, verbose, timeStampLogging);
    }

    /**
     * Format all transfered bytes in their hexadecimal representation. The
     * value of 83 will be printed as 53.
     *
     * @param serialPortSocket
     * @param logOs
     * @param verbose
     * @param timeStampLogging
     * @return the new instance with hex logging.
     * @throws FileNotFoundException
     */
    public static LoggingSerialPortSocket wrapWithHexOutputStream(SerialPortSocket serialPortSocket, OutputStream logOs,
            boolean verbose, TimeStampLogging timeStampLogging) throws FileNotFoundException, IOException {
        return new LoggingSerialPortSocket(serialPortSocket, logOs, false, verbose, timeStampLogging);
    }

    private LIS lis;

    private final LogWriter logWriter;

    private LOS los;
    final private SerialPortSocket serialPortSocket;

    /**
     *
     * @see #wrapWithAsciiOutputStream(SerialPortSocket, OutputStream, boolean,
     * TimeStampLogging)
     * @see #wrapWithHexOutputStream(SerialPortSocket, OutputStream, boolean,
     * TimeStampLogging)
     *
     * @param serialPortSocket
     * @param logOs
     * @param ascii
     * @param verbose
     * @param timeStampLogging
     * @throws FileNotFoundException
     */
    private LoggingSerialPortSocket(SerialPortSocket serialPortSocket, OutputStream logOs, boolean ascii,
            boolean verbose, TimeStampLogging timeStampLogging) throws FileNotFoundException, IOException {
        this.serialPortSocket = serialPortSocket;
        this.logWriter = new LogWriter(logOs, ascii, timeStampLogging, verbose);
        cleaner.register(this, new LogWriterCloser(this.logWriter));

        logWriter.spOpend(Instant.now(), serialPortSocket.getPortName(), "speed=" + serialPortSocket.getSpeed() + ", dataBits="
                + serialPortSocket.getDatatBits() + ", stopBits=" + serialPortSocket.getStopBits() + ", partity=" + serialPortSocket.getParity() + ", flowControl=" + serialPortSocket.getFlowControl());
    }

    @Override
    public void close() throws IOException {
        los = null;
        lis = null;
        logWriter.beforeSpClose(Instant.now());
        try {
            serialPortSocket.close();
            logWriter.afterSpClose(Instant.now());
        } catch (IOException e) {
            logWriter.afterSpClose(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void drainOutputBuffer() throws IOException {
        logWriter.beforeDrainOutputBuffer(Instant.now());
        try {
            serialPortSocket.drainOutputBuffer();
            logWriter.afterDrainOutputBuffer(Instant.now());
        } catch (IOException e) {
            logWriter.afterDrainOutputBuffer(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public Speed getSpeed() throws IOException {
        logWriter.beforeGetSpeed(Instant.now());
        try {
            final Speed result = serialPortSocket.getSpeed();
            logWriter.afterGetSpeed(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetSpeed(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public DataBits getDatatBits() throws IOException {
        logWriter.beforeGetDatatBits(Instant.now());
        try {
            final DataBits result = serialPortSocket.getDatatBits();
            logWriter.afterGetDatatBits(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetDatatBits(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public Set getFlowControl() throws IOException {
        logWriter.beforeGetFlowControl(Instant.now());
        try {
            final Set result = serialPortSocket.getFlowControl();
            logWriter.afterGetFlowControl(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetFlowControl(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public int getInBufferBytesCount() throws IOException {
        logWriter.beforeGetInBufferBytesCount(Instant.now());
        try {
            final int result = serialPortSocket.getInBufferBytesCount();
            logWriter.afterGetInBufferBytesCount(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetInBufferBytesCount(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public InputStream getInputStream() throws IOException {
        final InputStream ois = serialPortSocket.getInputStream();
        if (ois == null) {
            lis = null;
        } else if (lis == null) {
            lis = new LIS(ois);
        } else if (lis.is != ois) {
            lis = new LIS(ois);
        }
        return lis;
    }

    @Override
    public int getInterByteReadTimeout() throws IOException {
        logWriter.beforeGetInterByteReadTimeout(Instant.now());
        try {
            final int result = serialPortSocket.getInterByteReadTimeout();
            logWriter.afterGetInterByteReadTimeout(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetInterByteReadTimeout(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public int getOutBufferBytesCount() throws IOException {
        logWriter.beforeGetOutBufferBytesCount(Instant.now());
        try {
            final int result = serialPortSocket.getOutBufferBytesCount();
            logWriter.afterGetOutBufferBytesCount(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetOutBufferBytesCount(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        final OutputStream oos = serialPortSocket.getOutputStream();
        if (oos == null) {
            los = null;
        } else if (los == null) {
            los = new LOS(oos);
        } else if (los.os != oos) {
            los = new LOS(oos);
        }
        return los;
    }

    @Override
    public int getOverallReadTimeout() throws IOException {
        logWriter.beforeGetOverallReadTimeout(Instant.now());
        try {
            final int result = serialPortSocket.getOverallReadTimeout();
            logWriter.afterGetOverallReadTimeout(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetOverallReadTimeout(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public int getOverallWriteTimeout() throws IOException {
        logWriter.beforeGetOverallWriteTimeout(Instant.now());
        try {
            final int result = serialPortSocket.getOverallWriteTimeout();
            logWriter.afterGetOverallWriteTimeout(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetOverallWriteTimeout(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public Parity getParity() throws IOException {
        logWriter.beforeGetParity(Instant.now());
        try {
            final Parity result = serialPortSocket.getParity();
            logWriter.afterGetParity(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetParity(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public String getPortName() {
        return serialPortSocket.getPortName();
    }

    @Override
    public StopBits getStopBits() throws IOException {
        logWriter.beforeGetStopBits(Instant.now());
        try {
            final StopBits result = serialPortSocket.getStopBits();
            logWriter.afterGetStopBits(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetStopBits(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public char getXOFFChar() throws IOException {
        logWriter.beforeGetXOFFChar(Instant.now());
        try {
            final char result = serialPortSocket.getXOFFChar();
            logWriter.afterGetXOFFChar(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetXOFFChar(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public char getXONChar() throws IOException {
        logWriter.beforeGetXONChar(Instant.now());
        try {
            final char result = serialPortSocket.getXONChar();
            logWriter.afterGetXONChar(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterGetXONChar(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public boolean isCTS() throws IOException {
        logWriter.beforeIsCTS(Instant.now());
        try {
            final boolean result = serialPortSocket.isCTS();
            logWriter.afterIsCTS(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterIsCTS(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public boolean isDCD() throws IOException {
        logWriter.beforeIsDCD(Instant.now());
        try {
            final boolean result = serialPortSocket.isDCD();
            logWriter.afterIsDCD(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterIsDCD(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public boolean isDSR() throws IOException {
        logWriter.beforeIsDSR(Instant.now());
        try {
            final boolean result = serialPortSocket.isDSR();
            logWriter.afterIsDSR(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterIsDSR(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public boolean isOpen() {
        return serialPortSocket.isOpen();
    }

    @Override
    public boolean isRI() throws IOException {
        logWriter.beforeIsRI(Instant.now());
        try {
            final boolean result = serialPortSocket.isRI();
            logWriter.afterIsRI(Instant.now(), result);
            return result;
        } catch (IOException e) {
            logWriter.afterIsRI(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void sendBreak(int duration) throws IOException {
        logWriter.beforeSendBreak(Instant.now(), duration);
        try {
            serialPortSocket.sendBreak(duration);
            logWriter.afterSendBreak(Instant.now());
        } catch (IOException e) {
            logWriter.afterSendBreak(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void sendXOFF() throws IOException {
        logWriter.beforeSendXOFF(Instant.now());
        try {
            serialPortSocket.sendXOFF();
            logWriter.afterSendXOFF(Instant.now());
        } catch (IOException e) {
            logWriter.afterSendXOFF(Instant.now(), e);
            throw e;
        }
        serialPortSocket.sendXOFF();
    }

    @Override
    public void sendXON() throws IOException {
        logWriter.beforeSendXON(Instant.now());
        try {
            serialPortSocket.sendXON();
            logWriter.afterSendXON(Instant.now());
        } catch (IOException e) {
            logWriter.afterSendXON(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setSpeed(Speed speed) throws IOException {
        logWriter.beforeSetSpeed(Instant.now(), speed);
        try {
            serialPortSocket.setSpeed(speed);
            logWriter.afterSetSpeed(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetSpeed(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setBreak(boolean value) throws IOException {
        logWriter.beforeSetBreak(Instant.now(), value);
        try {
            serialPortSocket.setBreak(value);
            logWriter.afterSetBreak(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetBreak(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setDataBits(DataBits dataBits) throws IOException {
        logWriter.beforeSetDataBits(Instant.now(), dataBits);
        try {
            serialPortSocket.setDataBits(dataBits);
            logWriter.afterSetDataBits(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetDataBits(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setDTR(boolean value) throws IOException {
        logWriter.beforeSetDTR(Instant.now(), value);
        try {
            serialPortSocket.setDTR(value);
            logWriter.afterSetDTR(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetDTR(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setFlowControl(Set flowControls) throws IOException {
        logWriter.beforeSetFlowControl(Instant.now(), flowControls);
        try {
            serialPortSocket.setFlowControl(flowControls);
            logWriter.afterSetFlowControl(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetFlowControl(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setParity(Parity parity) throws IOException {
        logWriter.beforeSetParity(Instant.now(), parity);
        try {
            serialPortSocket.setParity(parity);
            logWriter.afterSetParity(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetParity(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setRTS(boolean value) throws IOException {
        logWriter.beforeSetRTS(Instant.now(), value);
        try {
            serialPortSocket.setRTS(value);
            logWriter.afterSetRTS(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetRTS(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setStopBits(StopBits stopBits) throws IOException {
        logWriter.beforeSetStopBits(Instant.now(), stopBits);
        try {
            serialPortSocket.setStopBits(stopBits);
            logWriter.afterSetStopBits(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetStopBits(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setTimeouts(int interByteReadTimeout, int overallReadTimeout, int overallWriteTimeout)
            throws IOException {
        logWriter.beforeSetTimeouts(Instant.now(), interByteReadTimeout, overallReadTimeout, overallWriteTimeout);
        try {
            serialPortSocket.setTimeouts(interByteReadTimeout, overallReadTimeout, overallWriteTimeout);
            logWriter.afterSetTimeouts(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetTimeouts(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setXOFFChar(char c) throws IOException {
        logWriter.beforeSetXOFFChar(Instant.now(), c);
        try {
            serialPortSocket.setXOFFChar(c);
            logWriter.afterSetXOFFChar(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetXOFFChar(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void setXONChar(char c) throws IOException {
        logWriter.beforeSetXONChar(Instant.now(), c);
        try {
            serialPortSocket.setXONChar(c);
            logWriter.afterSetXONChar(Instant.now());
        } catch (IOException e) {
            logWriter.afterSetXONChar(Instant.now(), e);
            throw e;
        }
    }

    @Override
    public void explainRead(String fmt, Object... args) {
        logWriter.explainRead(Instant.now(), fmt, args);
    }

    @Override
    public void explainRead(Throwable t) {
        logWriter.explainRead(Instant.now(), t);
    }

    @Override
    public void explainWrite(String fmt, Object... args) {
        logWriter.explainWrite(Instant.now(), fmt, args);
    }

    @Override
    public void explainWrite(Throwable t) {
        logWriter.explainWrite(Instant.now(), t);
    }

}