001package jmri.jmrix.ipocs;
002
003import java.io.IOException;
004import java.nio.ByteBuffer;
005import java.nio.channels.AsynchronousSocketChannel;
006import java.nio.channels.CompletionHandler;
007import java.util.ArrayList;
008import java.util.List;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import jmri.jmrix.ipocs.protocol.Message;
014import jmri.jmrix.ipocs.protocol.packets.ConnectionRequestPacket;
015import jmri.jmrix.ipocs.protocol.packets.ConnectionResponsePacket;
016import jmri.jmrix.ipocs.protocol.packets.Packet;
017
018/**
019 *
020 * @author Fredrik Elestedt Copyright (C) 2020
021 * @since 4.21.2
022 */
023public class IpocsClientHandler implements CompletionHandler<Integer, ByteBuffer> {
024
025  private final static Logger log = LoggerFactory.getLogger(IpocsClientHandler.class);
026  private final AsynchronousSocketChannel client;
027  private String unitId;
028  private final List<IpocsClientListener> clientListeners = new ArrayList<IpocsClientListener>();
029
030  public String getUnitId() {
031      return unitId;
032  }
033
034  public IpocsClientHandler(final AsynchronousSocketChannel client) {
035    this.client = client;
036    ByteBuffer recvBuffer = ByteBuffer.allocate(256);
037    client.read(recvBuffer, recvBuffer, this);
038  }
039
040  @Override
041  public void completed(final Integer bytesRead, final ByteBuffer recvBuffer) {
042    // connection closed by the server
043    if (bytesRead == -1) {
044      try {
045        client.close();
046        for (IpocsClientListener listener : clientListeners) {
047          listener.clientDisconnected(this);
048        }
049      } catch (final IOException ex) {
050        log.error("Unable to close client: {}", ex.getMessage());
051      }
052      return;
053    }
054    int currPos = recvBuffer.position();
055    recvBuffer.rewind();
056
057    Message msg = null;
058    while (recvBuffer.position() < currPos
059       && (msg = Message.parse(recvBuffer, currPos - recvBuffer.position())) != null) {
060      for (Packet pkt : msg.getPackets()) {
061        switch (pkt.getId()) {
062          case ConnectionRequestPacket.IDENT:
063            unitId = msg.getObjectName();
064            // TODO Check site data version
065            for (IpocsClientListener listener : clientListeners) {
066              listener.clientConnected(this);
067            }
068            Message response = new Message();
069            response.setObjectName(msg.getObjectName());
070            ConnectionResponsePacket respPkt = new ConnectionResponsePacket();
071            respPkt.setProtocolVersion(((ConnectionRequestPacket)pkt).getProtocolVersion());
072            response.getPackets().add(respPkt);
073            client.write(response.serialize());
074            break;
075          default:
076            for (IpocsClientListener listener : clientListeners) {
077              listener.onMessage(this, msg);
078            }
079            break;
080        }
081      }
082    }
083    ByteBuffer newRecvBuffer = ByteBuffer.allocate(256);
084    if (recvBuffer.position() < currPos) {
085      int position = recvBuffer.position();
086      newRecvBuffer.put(recvBuffer);
087      newRecvBuffer.position(currPos - position);
088    }
089    client.read(newRecvBuffer, newRecvBuffer, this);
090  }
091
092  @Override
093  public void failed(final Throwable exc, final ByteBuffer attachment) {
094    try {
095      client.close();
096    } catch (IOException ex) {
097      log.error("Error closing connection", ex);
098    }
099    for (IpocsClientListener listener : clientListeners) {
100      listener.clientDisconnected(this);
101    }
102  }
103
104  public void addClientListener(IpocsClientListener clientListener) {
105    clientListeners.add(clientListener);
106  }
107
108  public void removeClientListener(IpocsClientListener clientListener) {
109    clientListeners.remove(clientListener);
110  }
111
112  public void send(Message msg) {
113    client.write(msg.serialize());
114  }
115}