001package jmri.jmrix.ipocs;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.net.InetSocketAddress;
007import java.net.StandardSocketOptions;
008import java.nio.channels.AsynchronousServerSocketChannel;
009import java.util.ArrayList;
010import java.util.HashMap;
011import java.util.List;
012import java.util.Map;
013
014
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018import jmri.jmrix.AbstractPortController;
019import jmri.jmrix.ipocs.protocol.Message;
020import jmri.util.zeroconf.ZeroConfService;
021
022/**
023 *
024 * @author Fredrik Elestedt Copyright (C) 2020
025 * @since 4.21.2
026 */
027public class IpocsPortController extends AbstractPortController implements IpocsClientListener {
028  private final static Logger log = LoggerFactory.getLogger(IpocsPortController.class);
029  private static String INADDR_ANY = "0.0.0.0";
030  private short port = 0;
031  private AsynchronousServerSocketChannel serverSocket = null;
032  private IpocsSocketAcceptor socketAcceptor;
033  private ZeroConfService zeroConfService = null;
034  private final List<IpocsClientListener> clientListeners = new ArrayList<IpocsClientListener>();
035  private final Map<String, IpocsClientHandler> clients = new HashMap<>();
036  private final Map<String, Message> lastMessage = new HashMap<>();
037
038  public IpocsPortController(IpocsSystemConnectionMemo memo) {
039    super(memo);
040    super.setManufacturer(IpocsConnectionTypeList.IPOCSMR);
041    final Option o1 = new Option("Port", new String[]{"0"}, false, Option.Type.TEXT);
042    super.options.put(super.option1Name, o1);
043  }
044
045  @Override
046  public IpocsSystemConnectionMemo getSystemConnectionMemo() {
047    return (IpocsSystemConnectionMemo) super.getSystemConnectionMemo();
048  }
049
050  @Override
051  public void configure() {
052    if (getSystemConnectionMemo().getPortController() == null) {
053      getSystemConnectionMemo().setPortController(this);
054    }
055    getSystemConnectionMemo().configureManagers();
056  }
057
058  @Override
059  public void connect() throws IOException {
060    log.info("Setting up service");
061    serverSocket = AsynchronousServerSocketChannel.open();
062    socketAcceptor = new IpocsSocketAcceptor(this, serverSocket);
063    final InetSocketAddress address = new InetSocketAddress(INADDR_ANY, port);
064    serverSocket.bind(address);
065    serverSocket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
066    serverSocket.accept(null, socketAcceptor);
067    int servicePort = ((InetSocketAddress)serverSocket.getLocalAddress()).getPort();
068    log.info("Starting ZeroConfService _ipocs._tcp.local for port {}", servicePort);
069    zeroConfService = ZeroConfService.create("_ipocs._tcp.local.", "ipocs", servicePort, 0, 0, new HashMap<String, String>());
070    zeroConfService.publish();
071  }
072
073  @Override
074  public DataInputStream getInputStream() {
075    throw new UnsupportedOperationException();
076  }
077
078  @Override
079  public DataOutputStream getOutputStream() {
080    throw new UnsupportedOperationException();
081  }
082
083  @Override
084  public String getCurrentPortName() {
085    return "IPOCSMR";
086  }
087  
088  public void addListener(IpocsClientListener clientListener) {
089    clientListeners.add(clientListener);
090  }
091
092  public void removeListener(IpocsClientListener clientListener) {
093    clientListeners.remove(clientListener);
094  }
095
096  @Override
097  public void clientConnected(IpocsClientHandler client) {
098    log.info("New client connected");
099  }
100
101  @Override
102  public void clientDisconnected(IpocsClientHandler client) {
103    new HashMap<String, IpocsClientHandler>(clients).forEach((userName, storedClient) -> {
104      if (storedClient == client) {
105        clients.remove(userName);
106        lastMessage.remove(userName);
107        for (IpocsClientListener handler : clientListeners) {
108          if (userName.equals(handler.getUserName())) {
109            handler.clientDisconnected(client);
110          }
111        }
112      }
113    });
114  }
115
116  @Override
117  public void onMessage(IpocsClientHandler client, Message msg) {
118    clients.put(msg.getObjectName(), client);
119    lastMessage.put(msg.getObjectName(), msg);
120    for (IpocsClientListener handler : clientListeners) {
121      if (handler.getUserName().equals(msg.getObjectName())) {
122        handler.onMessage(client, msg);
123      }
124    }
125  }
126
127  public void send(Message msg) {
128    IpocsClientHandler client = clients.get(msg.getObjectName());
129    if (client != null) {
130      client.send(msg);
131    }
132  }
133
134  public Message getLastStatus(String userName) {
135    return lastMessage.get(userName);
136  }
137
138  public short getPort() {
139    return port;
140  }
141
142  public void setPort(short port) {
143      this.port = port;
144  }
145}