Package org.ugate.service

Source Code of org.ugate.service.WirelessService

package org.ugate.service;

import java.util.List;

import org.slf4j.Logger;
import org.ugate.ByteUtils;
import org.ugate.UGateEvent;
import org.ugate.UGateKeeper;
import org.ugate.UGateUtil;
import org.ugate.resources.RS;
import org.ugate.resources.RS.KEY;
import org.ugate.service.entity.Command;
import org.ugate.service.entity.EntityExtractor;
import org.ugate.service.entity.RemoteNodeType;
import org.ugate.service.entity.jpa.Actor;
import org.ugate.service.entity.jpa.RemoteNode;
import org.ugate.wireless.data.RxData;
import org.ugate.wireless.data.RxTxRemoteNodeDTO;
import org.ugate.wireless.data.UGateXBeePacketListener;

import com.rapplogic.xbee.api.AtCommand;
import com.rapplogic.xbee.api.AtCommandResponse;
import com.rapplogic.xbee.api.RemoteAtRequest;
import com.rapplogic.xbee.api.RemoteAtResponse;
import com.rapplogic.xbee.api.XBee;
import com.rapplogic.xbee.api.XBeeAddress16;
import com.rapplogic.xbee.api.XBeeException;
import com.rapplogic.xbee.api.XBeeTimeoutException;
import com.rapplogic.xbee.api.wpan.TxRequest16;
import com.rapplogic.xbee.api.wpan.TxStatusResponse;

/**
* Wireless service
*/
public class WirelessService extends ExtractorService<Actor> {

  /**
   * Constructor
   *
   * @param extractor
   *            the {@link #getExtractor()}
   */
  WirelessService(final EntityExtractor<Actor> extractor) {
    super(extractor);
  }

  private final Logger log = UGateUtil.getLogger(WirelessService.class);
  public static final int DEFAULT_WAIT_MILISECONDS = 12000;
  private XBee xbee;
  private UGateXBeePacketListener packetListener;
  private boolean requiresRestart;
  private boolean isListening;

  /**
   * Connects to the local wireless device
   *
   * @return true if the connection has been established
   */
  public boolean init() {
    if (xbee != null) {
      return true;
    }
    log.debug("Initializing local XBee");
    // ensure that the needed RXTX is installed/loaded (if not install/load it)
    requiresRestart = RS.initComm();
    if (requiresRestart) {
      return false;
    }
    xbee = new XBee();
    packetListener = new UGateXBeePacketListener() {
      @Override
      protected <V extends RxData> void handleEvent(final UGateEvent<RemoteNode, V> event) {
        UGateKeeper.DEFAULT.notifyListeners(event);
      }
    };
    // test the serial ports
    getSerialPorts();
    return true;
  }
 
  /**
   * Connects to the wireless network using the
   */
  public boolean connect() {
    if (xbee == null) {
      init();
    }
    if (isListening()) {
      disconnect();
    }
    if (extract() == null || extract().getHost() == null
        || extract().getHost().getId() <= 0) {
      return false;
    }
    if (log.isInfoEnabled()) {
      log.info(String.format("Connecting to local XBee @ %1$s/%2$s",
          extract().getHost().getComPort(), extract().getHost()
              .getComBaud()));
    }
    UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<WirelessService, Void>(
        this, UGateEvent.Type.WIRELESS_HOST_CONNECTING, false));
    try {
      xbee.open(extract().getHost().getComPort(), extract().getHost()
          .getComBaud());
      if (!isListening()) {
        xbee.addPacketListener(packetListener);
        isListening = true;
      }
      log.info(String
          .format("Connected to local XBee using address %1$s and baud rate %2$s",
              extract().getHost().getComAddress(),
              extract().getHost().getComBaud()));
      // XBee connection is blocking so notification can be sent here
      UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<WirelessService, Void>(
          this, UGateEvent.Type.WIRELESS_HOST_CONNECTED, false));
      return true;
    } catch (final Throwable t) {
      final String errorMsg = String.format(
          "Unable to establish a connection to the local XBee at address %1$s using port %2$s and baud rate %3$s",
          extract().getHost().getComAddress(),
          extract().getHost().getComPort(),
          extract().getHost().getComBaud());
      log.warn(errorMsg, t);
      UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<WirelessService, Void>(
          this, UGateEvent.Type.WIRELESS_HOST_CONNECT_FAILED, false, errorMsg, t.getMessage()));
      if (t instanceof XBeeException) {
        // bug in XBee connection that will show xbee.isConnected() as true after an XBeeException unless we close it here
        try {
          log.debug(String.format("Closing connection due to %1$s", XBeeException.class.getName()));
          wirelessDisconnectInternal(false);
        } catch (final Throwable t2) {
          log.warn(String.format("Unable to close %1$s connection (diconnecting because connection threw error",
              XBee.class.getName()), t2);
        }
      }
      return false;
    }
  }
 
  /**
   * Disconnects from the wireless network
   */
  public void disconnect() {
    wirelessDisconnectInternal(true);
  }
 
  /**
   * Disconnects from the wireless network
   *
   * @param notify true to notify listeners
   */
  private void wirelessDisconnectInternal(final boolean notify) {
    if (isConnected()) {
      String msg = "Disconnecting from XBee";
      log.info(msg);
      if (notify) {
        UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<WirelessService, Void>(
            WirelessService.this, UGateEvent.Type.WIRELESS_HOST_DISCONNECTING, false, msg))
      }
      try {
        xbee.close();
        msg = "Disconnected from XBee";
        log.info(msg);
        if (notify) {
          // XBee close is blocking so notification can be sent here
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<WirelessService, Void>(
              WirelessService.this, UGateEvent.Type.WIRELESS_HOST_DISCONNECTED, false, msg));
        }
      } catch (final Throwable t) {
        msg = "Unable to close wireless connection";
        log.error(msg, t);
        if (notify) {
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<WirelessService, Void>(
              WirelessService.this, UGateEvent.Type.WIRELESS_HOST_DISCONNECT_FAILED, false,
              msg, t.getMessage()));
        }
      }
    }
  }

  /**
   * @return true when connected to the wireless network
   */
  public boolean isConnected() {
    return xbee != null && xbee.isConnected();
  }

  /**
   * Sends the data string to the {@linkplain RemoteNode#getAddress()} in
   * ASCII format array
   *
   * @param remoteNode
   *            the {@linkplain RemoteNode} to send the data to
   * @param command
   *            the executing {@linkplain Command}
   * @param timeout
   *            the number of milliseconds that will be used to wait for a
   *            response before timing out
   * @param throwRuntimeException
   *            true to throw any {@linkplain Throwable} as a
   *            {@linkplain RuntimeException}
   * @param data
   *            the data string to send
   * @return true when successful
   */
  public boolean sendData(final RemoteNode remoteNode, final Command command,
      final int timeout, final boolean throwRuntimeException,
      final String data) {
    return sendData(remoteNode, command, timeout, throwRuntimeException,
        ByteUtils.stringToIntArray(data));
  }

  /**
   * Sends the data string to the {@linkplain RemoteNode#getAddress()} in
   * ASCII format array
   *
   * @param remoteNode
   *            the {@linkplain RemoteNode} to send the data to
   * @param command
   *            the executing {@linkplain Command}
   * @param timeout
   *            the number of milliseconds that will be used to wait for a
   *            response before timing out
   * @param throwRuntimeException
   *            true to throw any {@linkplain Throwable} as a
   *            {@linkplain RuntimeException}
   * @param data
   *            the data string to send
   * @return true when successful
   */
  public boolean sendData(final RemoteNode remoteNode, final Command command,
      final int timeout, final boolean throwRuntimeException,
      final List<Integer> data) {
    int[] dataInts = new int[data.size()];
    for (int i = 0; i < data.size(); i++) {
      dataInts[i] = data.get(i);
    }
    return sendData(remoteNode, command, timeout, throwRuntimeException,
        dataInts);
  }

  /**
   * Sends the data array to the {@linkplain RemoteNode#getAddress()}
   *
   * @param remoteNode
   *            the {@linkplain RemoteNode} to send the data to
   * @param command
   *            the executing {@linkplain Command}
   * @param timeout
   *            the number of milliseconds that will be used to wait for a
   *            response before timing out
   * @param throwRuntimeException
   *            true to throw any {@linkplain Throwable} as a
   *            {@linkplain RuntimeException}
   * @param data
   *            the data to send
   * @return true when successful
   */
  public boolean sendData(final RemoteNode remoteNode, final Command command,
      final int timeout, final boolean throwRuntimeException,
      final int... data) {
    return sendData(new UGateEvent<RemoteNode, int[]>(remoteNode,
        UGateEvent.Type.INITIALIZE, false, null, command, null, data),
        timeout, throwRuntimeException);
  }
 
  /**
   * Sends the data array to the {@linkplain RemoteNode#getAddress()}
   *
   * @param event
   *            the event that contains the <code>int</code> array of data to
   *            send {@linkplain UGateEvent#getNewValue()}, the
   *            {@linkplain UGateEvent#getCommand()}, and
   *            {@linkplain UGateEvent#getSource()} to send the data to
   * @param timeout
   *            the number of milliseconds that will be used to wait for a
   *            response before timing out
   * @param throwRuntimeException
   *            true to throw any {@linkplain Throwable} as a
   *            {@linkplain RuntimeException}
   * @return true when successful
   */
  private boolean sendData(final UGateEvent<RemoteNode, int[]> event,
      final int timeout, final boolean throwRuntimeException) {
    String message;
    if (!isConnected()) {
      message = RS.rbLabel(KEY.SERVICE_WIRELESS_CONNECTION_REQUIRED);
      if (throwRuntimeException) {
        throw new IllegalStateException(message);
      } else {
        log.error(message);
        return false;
      }
    }
    if (event.getSource() == null) {
      message = RS.rbLabel(KEY.SERVICE_WIRELESS_SEND_ADDY_UNDEFINED);
      if (throwRuntimeException) {
        throw new NullPointerException(message);
      } else {
        log.error(message);
        return false;
      }
    }
    int i = 0;
    int failureCount = 0;
    try {
      // bytes header command and status/failure code
      final int[] bytesHeader = new int[] { event.getCommand().getKey(), RxData.Status.NORMAL.ordinal() };
      final int[] bytes = event.getNewValue() != null && event.getNewValue().length > 0 ?
          UGateUtil.arrayConcatInt(bytesHeader, event.getNewValue()) : bytesHeader;
      final XBeeAddress16 xbeeAddress = getXbeeAddress(event.getSource().getAddress());
      // create a unicast packet to be delivered to the supplied address, with the pay load
      final TxRequest16 request = new TxRequest16(xbeeAddress, bytes);
      message = RS.rbLabel(KEY.SERVICE_WIRELESS_SENDING, bytes, event.getSource().getAddress());
      log.info(message);
      UGateKeeper.DEFAULT.notifyListeners(event.clone(UGateEvent.Type.WIRELESS_DATA_TX, i, message));
      // send the packet and wait up to 12 seconds for the transmit status reply
      final TxStatusResponse response = (TxStatusResponse) xbee.sendSynchronous(request,
          timeout <= 0 ? DEFAULT_WAIT_MILISECONDS : timeout);
      if (response.isSuccess()) {
        // packet was delivered successfully
        message = RS.rbLabel(KEY.SERVICE_WIRELESS_ACK_SUCCESS, bytes, event.getSource().getAddress(), response.getStatus());
        log.info(message);
        UGateKeeper.DEFAULT.notifyListeners(event.clone(UGateEvent.Type.WIRELESS_DATA_TX_ACK, i, message));
      } else {
        // packet was not delivered
        failureCount++;
        message = RS.rbLabel(KEY.SERVICE_WIRELESS_ACK_FAILED, bytes, event.getSource().getAddress(), response.getStatus());
        if (throwRuntimeException) {
          throw new RuntimeException(message);
        } else {
          log.error(message);
        }
        UGateKeeper.DEFAULT.notifyListeners(event.clone(UGateEvent.Type.WIRELESS_DATA_TX_ACK_FAILED, i, message));
      }
    } catch (XBeeTimeoutException e) {
      failureCount++;
      message = RS.rbLabel(KEY.SERVICE_WIRELESS_TX_TIMEOUT, event.getSource().getAddress());
      if (throwRuntimeException) {
        throw new RuntimeException(message, e);
      } else {
        log.error(message, e);
      }
      UGateKeeper.DEFAULT.notifyListeners(event.clone(UGateEvent.Type.WIRELESS_DATA_TX_FAILED, i, message));
    } catch (final Throwable t) {
      failureCount++;
      message = RS.rbLabel(KEY.SERVICE_WIRELESS_TX_FAILED, event.getSource().getAddress());
      if (throwRuntimeException) {
        throw new RuntimeException(message, t);
      } else {
        log.error(message, t);
      }
      UGateKeeper.DEFAULT.notifyListeners(event.clone(UGateEvent.Type.WIRELESS_DATA_TX_FAILED, i, message));
    }
    return failureCount == 0;
  }

  /**
   * Tests the {@link RemoteNode#getAddress()} of a remote device within the
   * wireless network
   *
   * @param remoteNode
   *            the {@linkplain RemoteNode} to test a connection for
   * @param timeout
   *            the number of milliseconds that will be used to wait for a
   *            response before timing out
   * @return true when the connection was successful
   */
  public boolean testRemoteConnection(final RemoteNode remoteNode,
      final int timeout) {
    if (!isConnected()) {
      return false;
    }
    try {
      AtCommandResponse response;
      int[] responseValue;
      final XBeeAddress16 remoteAddress = getXbeeAddress(remoteNode
          .getAddress());
      if (remoteAddress != null) {
        final RemoteAtRequest request = new RemoteAtRequest(
            remoteAddress, "MY");
        final RemoteAtResponse remoteResponse = (RemoteAtResponse) xbee
            .sendSynchronous(request,
                timeout <= 0 ? DEFAULT_WAIT_MILISECONDS
                    : timeout);
        response = remoteResponse;
      } else {
        response = (AtCommandResponse) xbee
            .sendSynchronous(new AtCommand("MY"));
      }
      if (response.isOk()) {
        responseValue = response.getValue();
        final String address = ByteUtils.toBase16(responseValue);
        log.info(String.format("Successfully got %1$s address %2$s",
            (remoteAddress != null ? "remote" : "local"), address));
        if (address != null && !address.isEmpty()) {
          return true;
        }
      } else {
        throw new XBeeException(
            "Failed to get remote address response. Status is "
                + response.getStatus());
      }
    } catch (final XBeeTimeoutException e) {
      log.warn("Timed out getting remote XBee address", e);
    } catch (final XBeeException e) {
      log.warn("Error getting remote XBee address", e);
    }
    return false;
  }
 
  /**
   * Gets a wireless {@linkplain XBeeAddress16} for a wireless
   * {@linkplain RemoteNode#getAddress()}
   *
   * @param rawAddress
   *            the {@linkplain RemoteNode#getAddress()} of the wireless node
   * @return the {@linkplain XBeeAddress16}
   */
  private XBeeAddress16 getXbeeAddress(final String rawAddress) {
    // final int xbeeRawAddress =
    // Integer.parseInt(preferences.get(wirelessAddressHexKey), 16);
    if (rawAddress.length() > RemoteNodeType.WIRELESS_ADDRESS_MAX_DIGITS) {
      throw new IllegalArgumentException(
          "Wireless address cannot be more than "
              + RemoteNodeType.WIRELESS_ADDRESS_MAX_DIGITS
              + " hex digits long");
    }
    final int msb = Integer.parseInt(rawAddress.substring(0, 2), 16);
    final int lsb = Integer.parseInt(rawAddress.substring(2, 4), 16);
    final XBeeAddress16 xbeeAddress = new XBeeAddress16(msb, lsb);
    return xbeeAddress;
  }
 
  /**
   * Synchronizes the locally hosted settings with the remote wireless node(s)
   *
   * @param timeout
   *            the number of milliseconds that will be used to wait for a
   *            response before timing out
   * @param throwRuntimeException
   *            true to throw any {@linkplain Throwable} as a
   *            {@linkplain RuntimeException}
   * @param remoteNode
   *            the {@linkplain RemoteNode}(s) to send the settings to
   * @return true when all node(s) have been updated successfully
   */
  public boolean sendSettings(final int timeout,
      final boolean throwRuntimeException, final RemoteNode... remoteNode) {
    boolean allSuccess = false;
    if (!isConnected()) {
      return allSuccess;
    }
    try {
      for (final RemoteNode rn : remoteNode) {
        final RxTxRemoteNodeDTO sd = new RxTxRemoteNodeDTO(rn);
        final int[] sendData = sd.getData();
        log.info(String.format("Attempting to send: %s", sd));
        final UGateEvent<RemoteNode, int[]> event = new UGateEvent<>(
            rn, UGateEvent.Type.INITIALIZE, false, null,
            Command.SENSOR_SEND_SETTINGS, null, sendData);
        if (sendData(event, timeout, throwRuntimeException)) {
          log.info(String.format("Settings sent to %1$s",
              rn.getAddress()));
          allSuccess = true;
        }
      }
    } catch (final Throwable t) {
      if (throwRuntimeException && t instanceof RuntimeException) {
        throw (RuntimeException) t;
      } else if (throwRuntimeException) {
        throw new RuntimeException("Error while sending settings", t);
      } else {
        log.error("Error while sending settings", t);
      }
    }
    return allSuccess;
  }

  /**
   * This method is used to get a list of all the available Serial ports
   * (note: only Serial ports are considered). Any one of the elements
   * contained in the returned {@link List} can be used as a parameter in
   * {@link #wirelessBtn(String)} or {@link #wirelessBtn(String, int)} to open
   * a Serial connection.
   *
   * @return A {@link List} containing {@link String}s showing all available
   *         Serial ports.
   */
  public List<String> getSerialPorts() {
    return RS.getSerialPorts();
  }

  /**
   * @return true if the application needs to be restarted before a wireless
   *         connection can be established
   */
  public boolean isRequiresRestart() {
    return requiresRestart;
  }

  /**
   * @return true when the service is listening to incoming wireless data
   */
  public boolean isListening() {
    return isListening;
  }
}
TOP

Related Classes of org.ugate.service.WirelessService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.