001package jmri.jmrix.ieee802154.xbee;
002
003import com.digi.xbee.api.connection.ConnectionType;
004import com.digi.xbee.api.connection.IConnectionInterface;
005
006import java.util.Arrays;
007
008import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
009
010import jmri.jmrix.AbstractSerialPortController.SerialPortDataListener;
011
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * Provide access to IEEE802.15.4 devices via a serial com port.
017 *
018 * @author Paul Bender Copyright (C) 2013,2023
019 */
020public class XBeeAdapter extends jmri.jmrix.ieee802154.serialdriver.SerialDriverAdapter implements IConnectionInterface, SerialPortDataListener {
021
022    private boolean iConnectionOpened = false;
023
024    public XBeeAdapter() {
025        super(new XBeeConnectionMemo());
026    }
027
028    @Override
029    public String openPort(String portName, String appName) {
030           // get and open the primary port
031           currentSerialPort = activatePort(portName,log);
032           // try to set it for serial
033           setSerialPort();
034
035        // report status
036        reportPortStatus(log,portName);
037        opened = true;
038        return null; // normal operation
039    }
040
041    /**
042     * Local method to do specific port configuration
043     */
044    @Override
045    protected void setSerialPort() {
046        log.debug("setSerialPort() called.");
047        // find the baud rate value, configure comm options
048        int baud = currentBaudNumber(mBaudRate);
049        setBaudRate(currentSerialPort,baud);
050        configureLeads(currentSerialPort,true,true);
051
052        // The following are required for the XBee API's input thread.
053        setDataListener(currentSerialPort,this);
054    }
055
056    /**
057     * Set up all of the other objects to operate connected to this port.
058     */
059    @Override
060    public void configure() {
061        log.debug("configure() called.");
062        XBeeTrafficController tc = new XBeeTrafficController();
063
064        // connect to the traffic controller
065        this.getSystemConnectionMemo().setTrafficController(tc);
066        tc.setAdapterMemo(this.getSystemConnectionMemo());
067        tc.connectPort(this);
068        this.getSystemConnectionMemo().configureManagers();
069    }
070
071    /**
072     * {@inheritDoc}
073     */
074    @Override
075    public String[] validBaudRates() {
076        return Arrays.copyOf(validSpeeds, validSpeeds.length);
077    }
078
079    /**
080     * {@inheritDoc}
081     */
082    @Override
083    public int[] validBaudNumbers() {
084        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
085    }
086
087    @Override
088    public XBeeConnectionMemo getSystemConnectionMemo() {
089        jmri.jmrix.ieee802154.IEEE802154SystemConnectionMemo m = super.getSystemConnectionMemo();
090        if (m instanceof XBeeConnectionMemo ) {
091           return (XBeeConnectionMemo) m;
092        } else {
093           throw new java.lang.IllegalArgumentException("System Connection Memo associated with this connection is not the right type.");
094        }
095    }
096
097    private final String[] validSpeeds = new String[]{Bundle.getMessage("Baud1200"),
098            Bundle.getMessage("Baud2400"), Bundle.getMessage("Baud4800"),
099            Bundle.getMessage("Baud9600"), Bundle.getMessage("Baud19200"),
100            Bundle.getMessage("Baud38400"), Bundle.getMessage("Baud57600"),
101            Bundle.getMessage("Baud115200")};
102    private final int[] validSpeedValues = new int[]{1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200};
103
104    @Override
105    public int defaultBaudIndex() {
106        return 0;
107    }
108
109    // methods for IConnectionInterface
110
111    @Override
112    public void close() {
113        closeSerialPort(currentSerialPort);
114        iConnectionOpened = false;
115    }
116
117    @Override
118    public int readData(byte[] b) throws java.io.IOException {
119       log.debug("read data called with {}", b);
120       return getInputStream().read(b);
121    }
122
123    @Override
124    public int readData(byte[] b,int off, int len) throws java.io.IOException {
125       log.debug("read data called with {} {} {}", b, off, len);
126       return getInputStream().read(b,off,len);
127    }
128
129    @Override
130    public void writeData(byte[] b) throws java.io.IOException {
131       log.debug("write data called with {}", b);
132       getOutputStream().write(b);
133    }
134
135    @Override
136    public void writeData(byte[] b,int off, int len) throws java.io.IOException {
137       log.debug("write data called with {} {} {}", b, off, len);
138       getOutputStream().write(b,off,len);
139    }
140
141    @Override
142    public boolean isOpen(){
143       log.debug("isOpen called");
144       return ( iConnectionOpened );
145    }
146
147    @Override
148    public void open(){
149       log.debug("open called");
150       iConnectionOpened = true;
151       // don't do anything here.  We handle the details of open through the
152       // openPort call, which is called from the JMRI infrastructure.
153    }
154
155    @Override
156    public ConnectionType getConnectionType() {
157        return ConnectionType.UNKNOWN;
158    }
159
160    // SerialPortEventListener methods
161    @Override
162    public int getListeningEvents() {
163        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
164    }
165
166    @SuppressFBWarnings(value = {"NN_NAKED_NOTIFY"}, justification="The notify call is notifying the receive thread that data is available due to an event.")
167    @Override
168    public void serialEvent(SerialPortEvent serialPortEvent) {
169        if (serialPortEvent.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
170            return;
171        synchronized (this) {
172            this.notifyAll();
173        }
174    }
175
176    private final static Logger log = LoggerFactory.getLogger(XBeeAdapter.class);
177
178}