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