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

com.nu.art.io.BaseTransceiver Maven / Gradle / Ivy

/*
 * cyborg-core is an extendable  module based framework for Android.
 *
 * Copyright (C) 2018  Adam van der Kruk aka TacB0sS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.nu.art.io;

import com.nu.art.belog.Logger;
import com.nu.art.core.exceptions.runtime.BadImplementationException;
import com.nu.art.core.tools.ArrayTools;
import com.nu.art.core.utils.DebugFlags;
import com.nu.art.core.utils.DebugFlags.DebugFlag;
import com.nu.art.core.utils.JavaHandler;

import java.io.IOException;
import java.net.SocketException;

import static com.nu.art.io.ConnectionState.Connected;
import static com.nu.art.io.ConnectionState.Connecting;
import static com.nu.art.io.ConnectionState.Idle;

public abstract class BaseTransceiver
	extends Logger {

	private static final int Interval_Timeout = 10000;
	public static final DebugFlag DebugFlag = DebugFlags.createFlag(BaseTransceiver.class);

	protected SocketWrapper socket;

	private ConnectionState state = Idle;

	protected final String name;

	protected final PacketSerializer packetSerializer;

	private final JavaHandler receiver;

	private final JavaHandler timeout;

	private final JavaHandler transmitter;

	private boolean listen = true;

	private int sendingTimeout = Interval_Timeout;

	private boolean oneShot = false;

	private TransceiverListener[] listeners = {};

	private Runnable connectAndListen = new Runnable() {
		@Override
		public void run() {
			try {
				listen = true;
				while (listen) {
					logDebug("live");

					setState(Connecting);
					receiver.clear();

					socket = connectImpl();
					setState(Connected);

					while (socket.isConnected()) {
						try {
							logDebug("waiting for packet");
							processPacket();
						} catch (SocketException e) {
							logError("SocketException");

							break;
						} catch (IOException e) {
							logError("IOException");

							try {
								socket.close();
							} catch (IOException e1) {
								notifyError(e);
							}
						} catch (Exception e) {
							notifyError(e);
							logError("inner Exception");
						}
					}
					if (oneShot)
						break;
				}
			} catch (Exception e) {
				logError("outer Exception");
				notifyError(e);
			} finally {
				logDebug("died");

				_disconnectImpl();
			}
		}
	};

	private void _disconnectImpl() {
		logInfo("+---+ Disconnecting...");
		setState(ConnectionState.Disconnecting);
		disconnectImpl();
		try {
			if (socket != null)
				socket.close();
		} catch (IOException e) {
			notifyError(e);
		}

		socket = null;
		setState(Idle);
	}

	@Override
	protected boolean isLoggerEnabled() {
		return DebugFlag.isEnabled();
	}

	public final void disconnect() {
		if (state == Idle) {
			logWarning("Cannot disconnect, State is Disconnected");
			return;
		}

		listen = false;
		_disconnectImpl();
	}

	protected void disconnectImpl() {}

	public BaseTransceiver(String name, PacketSerializer packetSerializer) {
		this.name = name;
		setTag(name);
		this.packetSerializer = packetSerializer;
		timeout = new JavaHandler().start("tx-timeout-" + name);
		transmitter = new JavaHandler().start("tx-" + name);
		receiver = new JavaHandler().start("rx-" + name);
	}

	public final void setOneShot() {
		oneShot = true;
	}

	public final void sendPacket(final Packet packet) {
		sendPacket(packet, true);
	}

	public final void sendPacket(final Packet packet, final boolean printToLog) {
		transmitter.post(new Runnable() {
			@Override
			public void run() {
				try {
					sendPacketSync(packet, printToLog);
				} catch (IOException e) {
					notifyError(e);
				}
			}
		});
	}

	public void sendPacketSync(Packet packet)
		throws IOException {
		sendPacketSync(packet, true);
	}

	public void sendPacketSync(final Packet packet, boolean printToLog)
		throws IOException {
		SocketWrapper tmpSocket = socket;
		if (tmpSocket == null)
			throw new IOException("Socket is null ignoring packet: " + packet);

		if (printToLog)
			logInfo("Sending packet to remote device: " + packet);

		final Thread callingThread = Thread.currentThread();
		Runnable timeoutTrigger = new Runnable() {
			@Override
			public void run() {
				logWarning("Interrupting thread '" + callingThread.getName() + "' Packet timed out: " + packet);
				callingThread.interrupt();
				disconnect();
			}
		};

		timeout.post(sendingTimeout, timeoutTrigger);
		try {
			packetSerializer.serializePacket(tmpSocket.getOutputStream(), packet);
		} finally {
			if (callingThread.isInterrupted())
				logError("Thread interrupted sending packet");
			timeout.remove(timeoutTrigger);
		}
	}

	public void connect() {
		if (!isState(Idle))
			return;

		logInfo("Connecting");
		receiver.remove(connectAndListen);
		receiver.post(connectAndListen);
	}

	public void dispose() {
		timeout.stop();
		transmitter.stop();
		receiver.stop();
	}

	private void processPacket()
		throws IOException {
		Packet packet = null;
		try {
			packet = packetSerializer.extractPacket(socket.getInputStream());
		} catch (IOException e) {
			throw e;
		} catch (Exception e) {
			this.notifyError(new BadImplementationException("Error while parsing packet", e));
		}
		logDebug("Process packet: " + packet);
		notifyNewPacket(packet);
	}

	public final void addListener(TransceiverListener listener) {
		if (listener == null)
			return;

		listeners = ArrayTools.appendElement(listeners, listener);
	}

	public final void removeListener(TransceiverListener listener) {
		if (listener == null)
			return;

		listeners = ArrayTools.removeElement(listeners, listener);
	}

	protected ConnectionState getState() {
		return state;
	}

	public synchronized boolean isState(ConnectionState state) {
		return this.state == state;
	}

	public final synchronized void setState(ConnectionState newState) {
		if (state == newState)
			return;

		logDebug("State changed: " + state + " => " + newState + ": " + extraLog());
		this.state = newState;
		notifyStateChanged(newState);
	}

	protected String extraLog() {
		return "";
	}

	protected abstract SocketWrapper connectImpl()
		throws Exception;

	protected final void notifyError(Exception e) {
		for (TransceiverListener listener : listeners) {
			listener.onError(e);
		}
	}

	protected final void notifyNewPacket(Packet packet) {
		for (TransceiverListener listener : listeners) {
			listener.onIncomingPacket(packet);
		}
	}

	protected final void notifyStateChanged(ConnectionState state) {
		for (TransceiverListener listener : listeners) {
			listener.onStateChange(state);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy