001package jmri.jmris;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.io.IOException;
006import java.util.HashMap;
007import java.util.Map;
008import jmri.InstanceManager;
009import jmri.JmriException;
010import jmri.Reporter;
011
012/**
013 * Abstract interface between the a JMRI reporter and a network connection
014 *
015 * @author Paul Bender Copyright (C) 2010
016 * @author Randall Wood Copyright (C) 2013
017 */
018abstract public class AbstractReporterServer {
019
020    private final HashMap<String, ReporterListener> reporters;
021
022    public AbstractReporterServer() {
023        reporters = new HashMap<>();
024    }
025
026    /*
027     * Protocol Specific Abstract Functions
028     */
029    abstract public void sendReport(String reporter, Object r) throws IOException;
030
031    abstract public void sendErrorStatus(String reporter) throws IOException;
032
033    abstract public void parseStatus(String statusString) throws JmriException, IOException;
034
035    synchronized protected void addReporterToList(String reporterName) {
036        if (!reporters.containsKey(reporterName)) {
037            reporters.put(reporterName, new ReporterListener(reporterName));
038            Reporter reporter = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(reporterName);
039            if(reporter!=null) {
040               reporter.addPropertyChangeListener(reporters.get(reporterName));
041            }
042        }
043    }
044
045    synchronized protected void removeReporterFromList(String reporterName) {
046        if (reporters.containsKey(reporterName)) {
047            Reporter reporter = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(reporterName);
048            if(reporter!=null) {
049               reporter.removePropertyChangeListener(reporters.get(reporterName));
050            }
051            reporters.remove(reporterName);
052        }
053    }
054
055    public Reporter initReporter(String reporterName) throws IllegalArgumentException {
056        Reporter reporter = InstanceManager.getDefault(jmri.ReporterManager.class).provideReporter(reporterName);
057        this.addReporterToList(reporterName);
058        return reporter;
059    }
060
061    /*
062     * Set the report state of a reporter
063     * 
064     * @parm reporterName - the name of a reporter
065     * @parm r - the object containing the report (currently a string).
066     */
067    public void setReporterReport(String reporterName, Object r) {
068        Reporter reporter;
069        // load address from reporterAddrTextField
070        try {
071            addReporterToList(reporterName);
072            reporter = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(reporterName);
073            if (reporter == null) {
074                log.error("Reporter {} is not available", reporterName);
075            } else {
076                log.debug("about to set reporter State");
077                reporter.setReport(r);
078            }
079        } catch (Exception ex) {
080            log.error("set reporter report", ex);
081        }
082    }
083
084    public void dispose() {
085        for (Map.Entry<String, ReporterListener> entry: this.reporters.entrySet()) {
086            Reporter reporter = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(entry.getKey());
087            if(reporter!=null) {
088               reporter.removePropertyChangeListener(entry.getValue());
089            }
090        }
091        this.reporters.clear();
092    }
093
094    class ReporterListener implements PropertyChangeListener {
095
096        ReporterListener(String reporterName) {
097            name = reporterName;
098            reporter = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(reporterName);
099        }
100
101        // update state as state of reporter changes
102        @Override
103        public void propertyChange(PropertyChangeEvent e) {
104            // If the Commanded State changes, show transition state as "<inconsistent>"
105            if (e.getPropertyName().equals("currentReport")) {
106                var n = e.getNewValue();
107                String now = null; // current report might be null
108                if ( n != null ){
109                    now = n.toString();
110                }
111                try {
112                    sendReport(name, now);
113                } catch (IOException ie) {
114                    log.debug("Error Sending Status");
115                    // if we get an error, de-register
116                    reporter.removePropertyChangeListener(this);
117                    removeReporterFromList(name);
118                }
119            }
120        }
121        String name = null;
122        Reporter reporter = null;
123    }
124
125    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractReporterServer.class);
126
127}