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