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