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