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}