001package jmri.jmrix.dcc4pc; 002 003import java.util.function.Supplier; 004import javax.annotation.CheckForNull; 005import javax.annotation.Nonnull; 006import jmri.AddressedProgrammer; 007import jmri.AddressedProgrammerManager; 008import jmri.CommandStation; 009import jmri.GlobalProgrammerManager; 010import jmri.managers.DefaultProgrammerManager; 011 012/** 013 * DCC4PC Programmer acts as a proxy for ops mode programming. Another 014 * connection (the one chosen with the connection's "Programmer" option) sends 015 * the Programming on Main packets, and the decoder's RailCom reply comes back 016 * through the RailCom reader; see {@link Dcc4PcOpsModeProgrammer}. 017 * <p> 018 * Only ops mode is provided. Service mode stays with the other connection. 019 * 020 * @see jmri.managers.DefaultProgrammerManager 021 * @author Kevin Dickerson Copyright (C) 2012 022 * 023 */ 024public class Dcc4PcProgrammerManager extends DefaultProgrammerManager { 025 026 /** 027 * Finds the other connection's programmer manager, or null if that 028 * connection isn't running (yet). Looked up when needed, so this manager 029 * can exist before the other connection has started. 030 */ 031 private final Supplier<AddressedProgrammerManager> manager; 032 033 /** Finds the other connection's command station, which sends POM read packets; null if none. */ 034 private final Supplier<CommandStation> commandStation; 035 036 /** 037 * @param manager finds the other connection's programmer manager 038 * @param commandStation finds the other connection's command station 039 * @param memo the DCC4PC connection, which gives this manager its name 040 */ 041 public Dcc4PcProgrammerManager(@Nonnull Supplier<AddressedProgrammerManager> manager, 042 @Nonnull Supplier<CommandStation> commandStation, @Nonnull Dcc4PcSystemConnectionMemo memo) { 043 super(null, memo); 044 this.manager = manager; 045 this.commandStation = commandStation; 046 } 047 048 /** 049 * @param manager the other connection's programmer manager 050 * @param memo the DCC4PC connection, which gives this manager its name 051 */ 052 public Dcc4PcProgrammerManager(@Nonnull AddressedProgrammerManager manager, @Nonnull Dcc4PcSystemConnectionMemo memo) { 053 this(() -> manager, () -> null, memo); 054 } 055 056 /** 057 * @param <T> a manager for both modes 058 * @param manager the other connection's programmer manager 059 */ 060 public <T extends AddressedProgrammerManager & GlobalProgrammerManager> Dcc4PcProgrammerManager(@Nonnull T manager) { 061 super(null); 062 this.manager = () -> manager; 063 this.commandStation = () -> null; 064 } 065 066 @CheckForNull 067 private AddressedProgrammerManager realManager() { 068 AddressedProgrammerManager m = manager.get(); 069 if (m == null) { 070 log.warn("{}: no connection to send Programming on Main packets; check the connection's" 071 + " Programmer option, or the default Command Station in Preferences, Defaults", getUserName()); 072 } 073 return m; 074 } 075 076 /** 077 * {@inheritDoc} 078 * <p> 079 * True until the other connection says otherwise: it may simply not have 080 * started yet. 081 */ 082 @Override 083 public boolean isAddressedModePossible() { 084 AddressedProgrammerManager m = manager.get(); 085 return m == null || m.isAddressedModePossible(); 086 } 087 088 @Override 089 public boolean isGlobalProgrammerAvailable() { 090 return false; 091 } 092 093 @Override 094 protected AddressedProgrammer getConcreteAddressedProgrammer(boolean pLongAddress, int pAddress) { 095 AddressedProgrammerManager m = realManager(); 096 return m == null ? null : new Dcc4PcOpsModeProgrammer(pLongAddress, pAddress, m, commandStation.get()); 097 } 098 099 @Override 100 protected AddressedProgrammer reserveConcreteAddressedProgrammer(boolean pLongAddress, int pAddress) { 101 // a reserved programmer must read through DCC4PC too 102 return getConcreteAddressedProgrammer(pLongAddress, pAddress); 103 } 104 105 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Dcc4PcProgrammerManager.class); 106}