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

tel.schich.javacan.AbstractCanChannel Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/*
 * The MIT License
 * Copyright © 2018 Phillip Schichtel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package tel.schich.javacan;

import java.io.IOException;
import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;

import tel.schich.javacan.linux.UnixFileDescriptor;
import tel.schich.javacan.option.CanSocketOption;
import tel.schich.javacan.select.NativeChannel;
import tel.schich.javacan.select.NativeHandle;

/**
 * This abstract base class for CAN channels implements all shared APIs common to CAN communication: It implements
 * {@link java.nio.channels.SelectableChannel} via {@link java.nio.channels.spi.AbstractSelectableChannel}, implements
 * {@link tel.schich.javacan.select.NativeChannel} by exposing the underlying socket file descriptor as a
 * {@link tel.schich.javacan.linux.UnixFileDescriptor} and it provides APIs to set socket options and read/write
 * buffers.
 */
public abstract class AbstractCanChannel extends AbstractSelectableChannel implements NativeChannel {

    private final int sock;
    private final UnixFileDescriptor fileDescriptor;

    public AbstractCanChannel(SelectorProvider provider, int sock) {
        super(provider);
        this.sock = sock;
        this.fileDescriptor = new UnixFileDescriptor(sock);
    }

    /**
     * Returns the CAN device this channel is bound to, given that is has already been bound.
     *
     * @return The CAN device.
     * @throws java.nio.channels.NotYetBoundException if not yet bound
     */
    public abstract NetworkDevice getDevice();

    /**
     * Returns if this channel has already been bound. This state may in theory get out of sync with the kernel as
     * this is tracked purely in the {@link java.nio.channels.Channel} implementation, the kernel does not expose
     * its bind state.
     *
     * @return true if the socket has been bound
     */
    public abstract boolean isBound();

    /**
     * Returns the internal socket file descriptor
     *
     * @return the file descriptor
     */
    protected int getSocket() {
        return sock;
    }

    @Override
    public NativeHandle getHandle() {
        return fileDescriptor;
    }

    @Override
    protected void implCloseSelectableChannel() throws IOException {
        SocketCAN.close(sock);
    }

    @Override
    protected void implConfigureBlocking(boolean block) throws IOException {
        SocketCAN.setBlockingMode(sock, block);
    }

    @Override
    public int validOps() {
        return SelectionKey.OP_READ | SelectionKey.OP_WRITE;
    }

    /**
     * Sets a socket option on this {@link java.nio.channels.Channel}'s socket.
     *
     * @param option The option to set
     * @param value the value to set
     * @param  The type of the option
     * @return fluent interface, implementations may specialize
     * @throws IOException if the native call fails
     */
    public  AbstractCanChannel setOption(SocketOption option, T value) throws IOException {
        if (!isOpen()) {
            throw new ClosedChannelException();
        }
        if (option instanceof CanSocketOption) {
            ((CanSocketOption) option).getHandler().set(getHandle(), value);
            return this;
        } else {
            throw new IllegalArgumentException("option " + option.name() + " is not supported by CAN channels!");
        }
    }

    /**
     * Retrieves the current value of a socket option.
     * The returned value may or may not be useful depending on the state the socket is in.
     *
     * @param option The option to get
     * @param  The type of the option
     * @return The current value of option
     * @throws IOException if the native call fails
     */
    public  T getOption(SocketOption option) throws IOException {
        if (!isOpen()) {
            throw new ClosedChannelException();
        }
        if (option instanceof CanSocketOption) {
            return ((CanSocketOption) option).getHandler().get(getHandle());
        } else {
            throw new IllegalArgumentException(option.name() + " is no support by CAN channels!");
        }
    }

    /**
     * Reads data from this socket into the given {@link java.nio.ByteBuffer}.
     * The {@link java.nio.ByteBuffer} must be a direct buffer as it is passed into native code.
     * Buffer position and limit will be respected and the position will be updated.
     *
     * @param buffer the buffer to read into
     * @return The number of bytes read from the socket
     * @throws IOException if the native call fails
     */
    protected long readSocket(ByteBuffer buffer) throws IOException {
        if (!buffer.isDirect()) {
            throw new IllegalArgumentException("The buffer must be a direct buffer!");
        }
        int bytesRead = 0;
        begin();
        try {
            int pos = buffer.position();
            bytesRead = (int)SocketCAN.read(sock, buffer, pos, buffer.remaining());
            buffer.position(pos + bytesRead);
            return bytesRead;
        } finally {
            end(bytesRead > 0);
        }
    }

    /**
     * Writes data to this socket from the given {@link java.nio.ByteBuffer}.
     * The {@link java.nio.ByteBuffer} must be a direct buffer as it is passed into native code.
     * Buffer position and limit will be respected and the position will be updated.
     *
     * @param buffer the buffer to write from
     * @return The number of bytes written to the socket
     * @throws IOException if the native call fails
     */
    protected long writeSocket(ByteBuffer buffer) throws IOException {
        if (!buffer.isDirect()) {
            throw new IllegalArgumentException("The buffer must be a direct buffer!");
        }
        int bytesWritten = 0;
        begin();
        try {
            int pos = buffer.position();
            bytesWritten = (int) SocketCAN.write(sock, buffer, pos, buffer.remaining());
            buffer.position(pos + bytesWritten);
            return bytesWritten;
        } finally {
            end(bytesWritten > 0);
        }
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "(device=" + getDevice() + ", handle=" + getHandle() + ")";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy