001package jmri.jmrix.loconet;
002
003import java.util.Comparator;
004import java.util.ResourceBundle;
005import javax.annotation.Nonnull;
006
007import jmri.*;
008import jmri.jmrix.ConfiguringSystemConnectionMemo;
009import jmri.jmrix.DefaultSystemConnectionMemo;
010import jmri.jmrix.debugthrottle.DebugThrottleManager;
011import jmri.jmrix.loconet.swing.LnComponentFactory;
012import jmri.jmrix.swing.ComponentFactory;
013import jmri.managers.DefaultProgrammerManager;
014import jmri.util.NamedBeanComparator;
015
016import org.slf4j.Logger;
017import org.slf4j.LoggerFactory;
018
019/**
020 * Lightweight class to denote that a system is active, and provide general
021 * information.
022 * <p>
023 * Objects of specific subtypes are registered in the instance manager to
024 * activate their particular system.
025 *
026 * @author Bob Jacobsen Copyright (C) 2010
027 */
028public class LocoNetSystemConnectionMemo extends DefaultSystemConnectionMemo implements ConfiguringSystemConnectionMemo {
029
030    /**
031     * Must manually register() after construction is complete.
032     * @param lt Traffic controller to be used
033     * @param sm Slot Manager to be used
034     */
035    public LocoNetSystemConnectionMemo(LnTrafficController lt, SlotManager sm) {
036        super("L", "LocoNet"); // NOI18N
037        this.lt = lt;
038
039        this.sm = sm; // doesn't full register, but fine for this purpose.
040
041        // self-registration is deferred until the command station type is set below
042
043        // create and register the ComponentFactory for the GUI
044        InstanceManager.store(cf = new LnComponentFactory(this),
045                ComponentFactory.class);
046    }
047
048    /**
049     * Must manually register() after construction is complete.
050     */
051    public LocoNetSystemConnectionMemo() {
052        this("L", "LocoNet"); // NOI18N
053    }
054
055    public LocoNetSystemConnectionMemo(@Nonnull String prefix, @Nonnull String name) {
056        super(prefix, name); // NOI18N
057
058        // create and register the ComponentFactory for the GUI
059        InstanceManager.store(cf = new LnComponentFactory(this),
060                ComponentFactory.class);
061    }
062
063    /**
064     * Do both the default parent
065     * {@link jmri.SystemConnectionMemo} registration,
066     * and register this specific type.
067     */
068    @Override
069    public void register() {
070        super.register(); // registers general type
071        InstanceManager.store(this, LocoNetSystemConnectionMemo.class); // also register as specific type
072    }
073
074    ComponentFactory cf;
075    private LnTrafficController lt;
076    protected LocoNetThrottledTransmitter tm;
077    private SlotManager sm;
078    private LncvDevicesManager lncvdm = null;
079    private Lnsv1DevicesManager lnsv1dm = null;
080    private LnMessageManager lnm = null;
081    private Ln7gAccyRoutesManager ln7gAcRtm;
082
083    /**
084     * Provide access to the SlotManager for this particular connection.
085     *
086     * @return the slot manager or null if no valid slot manager is available
087     */
088    public SlotManager getSlotManager() {
089        if (sm == null) {
090            log.debug("slot manager is null, but there should always be a valid SlotManager", new Exception("Traceback"));
091        }
092        return sm;
093    }
094
095    /**
096     * Provide access to the TrafficController for this particular connection.
097     *
098     * @return the LocoNet-specific TrafficController
099     */
100    public LnTrafficController getLnTrafficController() {
101        if (lt == null) {
102            setLnTrafficController(new LnPacketizer(this)); // default to Packetizer TrafficController
103            log.debug("Auto create of LnTrafficController for initial configuration");
104        }
105        return lt;
106    }
107
108    public void setLnTrafficController(LnTrafficController lt) {
109        this.lt = lt;
110    }
111
112    public LnMessageManager getLnMessageManager() {
113        // create when needed
114        if (lnm == null) {
115            lnm = new LnMessageManager(getLnTrafficController());
116        }
117        return lnm;
118    }
119
120    public DefaultProgrammerManager getProgrammerManager() {
121        return (DefaultProgrammerManager) classObjectMap.computeIfAbsent(DefaultProgrammerManager.class,(Class<?> c) -> new LnProgrammerManager(this));
122    }
123
124    public void setProgrammerManager(DefaultProgrammerManager p) {
125        store(p,DefaultProgrammerManager.class);
126    }
127
128    public void setLncvDevicesManager(LncvDevicesManager lncvdm) {
129        this.lncvdm = lncvdm;
130    }
131
132    public void setLnsv1DevicesManager(Lnsv1DevicesManager lnsv1dm) {
133        this.lnsv1dm = lnsv1dm;
134    }
135
136    protected boolean mTurnoutNoRetry = false;
137    protected boolean mTurnoutExtraSpace = false;
138    protected boolean mInterrogateAtStart = true;
139
140    /**
141     * Configure the programming manager and "command station" objects.
142     *
143     * @param type               Command station type, used to configure various
144     *                           operations
145     * @param mTurnoutNoRetry    Is the user configuration set for no turnout
146     *                           operation retries?
147     * @param mTurnoutExtraSpace Is the user configuration set for extra time
148     *                           between turnout operations?
149     * @param mTranspondingAvailable    Is the layout configured to provide
150     *                                  transponding reports
151     * @param mInterrogate       Send interrogate messages at start up
152     * @param mLoconetProtocolAutoDetect Do we automatically detect the protocol to use or force LocoNet 1.1
153     */
154    public void configureCommandStation(LnCommandStationType type, boolean mTurnoutNoRetry,
155                                            boolean mTurnoutExtraSpace, boolean mTranspondingAvailable,
156                                            boolean mInterrogate, boolean mLoconetProtocolAutoDetect) {
157
158        // store arguments
159        this.mTurnoutNoRetry = mTurnoutNoRetry;
160        this.mTurnoutExtraSpace = mTurnoutExtraSpace;
161        this.mInterrogateAtStart = mInterrogate;
162
163        // create and install SlotManager
164        if (sm != null) {
165            log.error("Installing SlotManager twice", new Exception("TraceBack"));
166        }
167        sm = type.getSlotManager(lt);
168        if (sm != null) {
169            sm.setThrottledTransmitter(tm, mTurnoutNoRetry);
170
171            sm.setCommandStationType(type);
172            sm.setSystemConnectionMemo(this);
173            sm.setTranspondingAvailable(mTranspondingAvailable);
174            sm.setLoconetProtocolAutoDetect(mLoconetProtocolAutoDetect);
175
176            // store as CommandStation object
177            InstanceManager.store(sm, jmri.CommandStation.class);
178            store(sm, jmri.CommandStation.class);
179        }
180
181    }
182
183    /**
184     * Configure the common managers for LocoNet connections. This puts the
185     * common manager config in one place.
186     */
187    @Override
188    public void configureManagers() {
189
190        tm = new LocoNetThrottledTransmitter(getLnTrafficController(), mTurnoutExtraSpace);
191        log.debug("ThrottleTransmitted configured with: {}", mTurnoutExtraSpace);
192        if (sm != null) {
193            sm.setThrottledTransmitter(tm, mTurnoutNoRetry);
194            log.debug("set turnout retry: {}", mTurnoutNoRetry);
195        }
196
197        InstanceManager.store(getPowerManager(), PowerManager.class);
198
199        InstanceManager.setSensorManager(
200                getSensorManager());
201
202        InstanceManager.setTurnoutManager(
203                getTurnoutManager());
204
205        InstanceManager.setLightManager(
206                getLightManager());
207
208        InstanceManager.setStringIOManager(getStringIOManager());
209
210        InstanceManager.setThrottleManager(
211                getThrottleManager());
212
213        DefaultProgrammerManager programmerManager = getProgrammerManager();
214
215        if (programmerManager.isAddressedModePossible()) {
216            store(programmerManager, AddressedProgrammerManager.class);
217            InstanceManager.store(programmerManager, AddressedProgrammerManager.class);
218        }
219        if (programmerManager.isGlobalProgrammerAvailable()) {
220            store(getProgrammerManager(), GlobalProgrammerManager.class);
221            InstanceManager.store(getProgrammerManager(), GlobalProgrammerManager.class);
222        }
223
224        InstanceManager.setReporterManager(getReporterManager());
225
226        InstanceManager.setDefault(CabSignalManager.class,getCabSignalManager());
227
228        setConsistManager(new LocoNetConsistManager(this));
229
230        setLncvDevicesManager(new jmri.jmrix.loconet.LncvDevicesManager(this));
231
232        ClockControl cc = getClockControl();
233
234        InstanceManager.setDefault(ClockControl.class, cc);
235
236        getIdTagManager();
237
238        // register this SystemConnectionMemo to connect to rest of system
239        register();
240
241        // This must be done after the memo is registered
242        getPredefinedMeters();
243
244        // This must be done after the memo is registered
245        getThrottleStringIO();
246
247        ln7gAcRtm =
248                setLn7gAccyRoutesManager(
249                getLn7gAccyRoutesManager());
250        log.debug("Established Ln Accy Rt Mgr with memo {}",ln7gAcRtm.getMemo());
251
252    }
253
254    public LnPowerManager getPowerManager() {
255        if (getDisabled()) {
256            return null;
257        }
258        return (LnPowerManager) classObjectMap.computeIfAbsent(PowerManager.class,(Class<?> c) -> new LnPowerManager(this));
259    }
260
261    public ThrottleManager getThrottleManager() {
262        if (getSlotManager() != null) {
263            log.debug("GetThrottleManager for {}", getSlotManager().getCommandStationType());
264        }
265        if (getDisabled()) {
266            return null;
267        }
268        ThrottleManager throttleManager = get(ThrottleManager.class);
269        if (throttleManager == null && getSlotManager() != null) {
270            // ask command station type for specific throttle manager
271            LnCommandStationType cmdstation = getSlotManager().getCommandStationType();
272            log.debug("getThrottleManager constructs for {}", cmdstation.getName());
273            throttleManager = cmdstation.getThrottleManager(this);
274            assert throttleManager != null;
275            log.debug("result was type {}", throttleManager.getClass());
276            store(throttleManager,ThrottleManager.class);
277        }
278        return throttleManager;
279    }
280
281    public void setThrottleManager(ThrottleManager t) {
282        store(t,ThrottleManager.class);
283    }
284
285    public LnTurnoutManager getTurnoutManager() {
286        if (getDisabled()) {
287            return null;
288        }
289        return (LnTurnoutManager) classObjectMap.computeIfAbsent(TurnoutManager.class,(Class<?> c) -> new LnTurnoutManager(this, tm, mTurnoutNoRetry));
290    }
291
292    public LnClockControl getClockControl() {
293        if (getDisabled()) {
294            return null;
295        }
296        return (LnClockControl) classObjectMap.computeIfAbsent(ClockControl.class,(Class<?> c) -> new LnClockControl(this));
297    }
298
299    public LnReporterManager getReporterManager() {
300        if (getDisabled()) {
301            return null;
302        }
303        return (LnReporterManager) classObjectMap.computeIfAbsent(ReporterManager.class, (Class<?> c) -> new LnReporterManager(this));
304    }
305
306    public LnSensorManager getSensorManager() {
307        if (getDisabled()) {
308            return null;
309        }
310        return (LnSensorManager) classObjectMap.computeIfAbsent(SensorManager.class, (Class<?> c) -> new LnSensorManager(this, mInterrogateAtStart));
311    }
312
313    public LnLightManager getLightManager() {
314        if (getDisabled()) {
315            return null;
316        }
317        return (LnLightManager) classObjectMap.computeIfAbsent(LightManager.class, (Class<?> c) -> new LnLightManager(this));
318    }
319
320    public Lnsv1DevicesManager getLnsv1DevicesManager() {
321        if (getDisabled()) {
322            return null;
323        }
324        if (lnsv1dm == null) {
325            setLnsv1DevicesManager(new Lnsv1DevicesManager(this));
326            log.debug("Auto create of Lnsv1DevicesManager for initial configuration");
327        }
328        return lnsv1dm;
329    }
330
331    public LncvDevicesManager getLncvDevicesManager() {
332        if (getDisabled()) {
333            return null;
334        }
335        if (lncvdm == null) {
336            setLncvDevicesManager(new LncvDevicesManager(this));
337            log.debug("Auto create of LncvDevicesManager for initial configuration");
338        }
339        return lncvdm;
340    }
341
342    public LnStringIOManager getStringIOManager() {
343        if (getDisabled()) {
344            return null;
345        }
346        return (LnStringIOManager) classObjectMap.computeIfAbsent(StringIOManager.class, (Class<?> c) -> new LnStringIOManager(this));
347    }
348
349    public Ln7gAccyRoutesManager setLn7gAccyRoutesManager(Ln7gAccyRoutesManager ln7gaccyrm) {
350        this.ln7gAcRtm = ln7gaccyrm;
351        if (this.ln7gAcRtm != null) {
352            // this can only be true when getDisabled() == false
353            this.ln7gAcRtm.initContext(this);
354        }
355        return this.ln7gAcRtm;
356    }
357
358     public Ln7gAccyRoutesManager getLn7gAccyRoutesManager() {
359        if (getDisabled()) {
360            return null;
361        }
362        return (Ln7gAccyRoutesManager) classObjectMap.computeIfAbsent(Ln7gAccyRoutesManager.class,
363                (Class<?> c) -> new Ln7gAccyRoutesManager());
364    }
365
366    protected LnPredefinedMeters predefinedMeters;
367
368    public LnPredefinedMeters getPredefinedMeters() {
369        if (getDisabled()) {
370            log.warn("Aborting getPredefinedMeters account is disabled!");
371            return null;
372        }
373//        switch (getSlotManager().commandStationType) {
374//            case COMMAND_STATION_USB_DCS240_ALONE:
375//            case COMMAND_STATION_DCS240:
376//            case COMMAND_STATION_DCS210:
377//            case COMMAND_STATION_USB_DCS52_ALONE:
378//            case COMMAND_STATION_DCS052:
379//                break;
380//            default:
381//                // The command station does not support these meters
382//                return null;
383//        }
384        if (predefinedMeters == null) {
385            predefinedMeters = new LnPredefinedMeters(this);
386        }
387        return predefinedMeters;
388    }
389
390    LnThrottleStringIO throttleStringIO;
391
392    public void getThrottleStringIO() {
393        if (getDisabled()) {
394            log.warn("Aborting getThrottleStringIO account is disabled!");
395            return;
396        }
397        if (throttleStringIO == null) {
398            throttleStringIO = new LnThrottleStringIO(this);
399            InstanceManager.getDefault(jmri.StringIOManager.class)
400                    .register(throttleStringIO);
401        }
402    }
403
404    @Override
405    protected ResourceBundle getActionModelResourceBundle() {
406        return ResourceBundle.getBundle("jmri.jmrix.loconet.LocoNetActionListBundle");
407    }
408
409    @Override
410    public <B extends NamedBean> Comparator<B> getNamedBeanComparator(Class<B> type) {
411        return new NamedBeanComparator<>();
412    }
413
414    // yes, tagManager is static.  Tags can move between system connections.
415    // when readers are not all on the same LocoNet
416    // this manager is loaded on demand.
417    static TranspondingTagManager tagManager;
418
419    static public TranspondingTagManager getIdTagManager() {
420        synchronized (LocoNetSystemConnectionMemo.class) { // since tagManager can be null, can't synch on that
421            if (tagManager == null) {
422                tagManager = new TranspondingTagManager();
423                InstanceManager.setIdTagManager(tagManager);
424            }
425            return tagManager;
426        }
427    }
428
429    public LnCabSignalManager getCabSignalManager() {
430        return (LnCabSignalManager) classObjectMap.computeIfAbsent(CabSignalManager.class,(Class<?> c) -> new LnCabSignalManager(this));
431    }
432
433    @Override
434    public void dispose() {
435        if (throttleStringIO != null) {
436            throttleStringIO = null;
437        }
438        if (predefinedMeters != null) {
439            predefinedMeters.dispose();
440        }
441        LnPowerManager pm = (LnPowerManager) classObjectMap.get(PowerManager.class);
442        InstanceManager.deregister(this, LocoNetSystemConnectionMemo.class);
443        if (cf != null) {
444            InstanceManager.deregister(cf, ComponentFactory.class);
445            cf = null;
446        }
447        ThrottleManager throttleManager = get(ThrottleManager.class);
448        if (throttleManager != null) {
449            if (throttleManager instanceof LnThrottleManager) {
450                InstanceManager.deregister(((LnThrottleManager) throttleManager), LnThrottleManager.class);
451            } else if (throttleManager instanceof DebugThrottleManager) {
452                InstanceManager.deregister(((DebugThrottleManager) throttleManager), DebugThrottleManager.class);
453            }
454            deregister(throttleManager,ThrottleManager.class);
455        }
456
457        if (tm != null){
458            tm.dispose();
459            tm = null;
460        }
461        if (sm != null){
462            sm.dispose();
463            sm = null;
464        }
465        if (lt != null){
466            lt.dispose();
467            lt = null;
468        }
469        if (pm != null){
470            pm.dispose();
471        }
472        super.dispose();
473    }
474
475    private final static Logger log = LoggerFactory.getLogger(LocoNetSystemConnectionMemo.class);
476
477}