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