001package jmri.jmrix.dccpp; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import jmri.implementation.AbstractLight; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Implementation of the Light Object for DCC-EX 010 * <p> 011 * NOTE: This is a simplification of the DCCppTurnout class. 012 * <p> 013 * Based in part on SerialLight.java 014 * 015 * @author Paul Bender Copyright (C) 2008-2010 016 * @author Mark Underwood Copyright (C) 2015 017 */ 018public class DCCppLight extends AbstractLight implements DCCppListener { 019 020 @SuppressFBWarnings(value = "MS_PKGPROTECT", 021 justification = "Public for access by manager, tests, and SelectionPropertyDescriptor") 022 public static final String[] MODE_NAMES = {"Accessory Decoder", "CS VPIN"}; // NOI18N 023 024 public static final String CS_VPIN_MODE = MODE_NAMES[1]; 025 026 private DCCppTrafficController tc = null; 027 private DCCppLightManager lm = null; 028 029 /** 030 * Create a Light object, with only system name. 031 * <p> 032 * 'systemName' was previously validated in DCCppLightManager 033 * 034 * @param tc the traffic controller for the connection 035 * @param lm the managing LightManager for this Light 036 * @param systemName the system name for this Light 037 */ 038 public DCCppLight(DCCppTrafficController tc, DCCppLightManager lm, String systemName) { 039 super(systemName); 040 this.tc = tc; 041 this.lm = lm; 042 // Initialize the Light 043 initializeLight(systemName); 044 } 045 046 /** 047 * Create a Light object, with both system and user names. 048 * <p> 049 * 'systemName' was previously validated in DCCppLightManager 050 * 051 * @param tc the traffic controller for the connection 052 * @param lm the managing LightManager for this Light 053 * @param systemName the system name for this Light 054 * @param userName the user name for this Light 055 */ 056 public DCCppLight(DCCppTrafficController tc, DCCppLightManager lm, String systemName, String userName) { 057 super(systemName, userName); 058 this.tc = tc; 059 this.lm = lm; 060 // Initialize the Light 061 initializeLight(systemName); 062 } 063 064 /** 065 * Dispose of the light object. 066 */ 067 @Override 068 public void dispose() { 069 tc.removeDCCppListener(DCCppInterface.FEEDBACK | DCCppInterface.COMMINFO | DCCppInterface.CS_INFO, this); 070 super.dispose(); 071 } 072 073 /** 074 * Initialize the light object's parameters. 075 */ 076 private synchronized void initializeLight(String systemName) { 077 // Extract the Bit from the name 078 mAddress = lm.getBitFromSystemName(systemName); 079 // Set initial state 080 setState(OFF); 081 // At construction, register for messages 082 tc.addDCCppListener(DCCppInterface.FEEDBACK | DCCppInterface.COMMINFO | DCCppInterface.CS_INFO, this); 083 } 084 085 /** 086 * Sets up system dependent instance variables and set system independent 087 * instance variables to default values. 088 * <p> 089 * Note: most instance variables are in AbstractLight.java 090 */ 091 092 /** 093 * System dependent instance variables 094 */ 095 int mAddress = 0; // accessory output address 096 097 /* Internal State Machine states. */ 098 static final int OFFSENT = 1; 099 static final int COMMANDSENT = 2; 100 static final int IDLE = 0; 101 102 /** 103 * Set the current state of this Light. 104 * This routine requests the hardware to change. 105 */ 106 @Override 107 synchronized public void setState(int newState) { 108 if (newState != ON && newState != OFF) { 109 // Unsupported state 110 log.warn("Unsupported state {} requested for light {}", newState, getSystemName()); 111 return; 112 } 113 114 log.debug("Light Set State: mstate = {} newstate = {}", mState, newState); 115 116 // get the right packet 117 if (mAddress > 0) { 118 boolean state = (newState == jmri.Light.ON); 119 DCCppMessage msg; 120 Object modeProperty = getProperty(DCCppLightManager.DCCPP_LIGHT_MODE_KEY); 121 if (CS_VPIN_MODE.equals(modeProperty)) { 122 // CS VPIN: HIGH = ON, LOW = OFF 123 msg = DCCppMessage.makeOutputCmdMsgLC(mAddress, state); 124 } else { 125 if (modeProperty != null && !MODE_NAMES[0].equals(modeProperty)) { 126 log.warn("Unknown light mode '{}' for {}, using Accessory Decoder", modeProperty, getSystemName()); 127 } 128 msg = DCCppMessage.makeAccessoryDecoderMsg(mAddress, state); 129 } 130 tc.sendDCCppMessage(msg, this); 131 132 if (newState != mState) { 133 int oldState = mState; 134 mState = newState; 135 // notify listeners, if any 136 firePropertyChange("KnownState", oldState, newState); 137 } 138 } 139 } 140 141 /** 142 * {@inheritDoc} 143 * Handle an incoming message from the DCC-EX Base Station. 144 * <p> 145 * NOTE: We aren't registered as a listener, so this is only triggered 146 * when we send out a message 147 */ 148 @Override 149 synchronized public void message(DCCppReply l) { 150 log.debug("received message: {}", l); 151 // We don't expect a reply, so we don't do anything with replies. 152 } 153 154 /** 155 * {@inheritDoc} 156 * Listen for messages to the DCC-EX Base Station. 157 */ 158 @Override 159 public void message(DCCppMessage l) { 160 // messages not handled by DCCpp lights 161 } 162 163 // Handle a timeout notification 164 @Override 165 public void notifyTimeout(DCCppMessage msg) { 166 log.debug("Notified of timeout on message '{}'", msg); 167 } 168 169 private static final Logger log = LoggerFactory.getLogger(DCCppLight.class); 170 171}