001package jmri.jmrix.mqtt; 002 003import java.util.EnumSet; 004import java.util.HashMap; 005 006import jmri.DccThrottle; 007import jmri.LocoAddress; 008import jmri.SpeedStepMode; 009import jmri.ThrottleListener; 010import jmri.jmrix.AbstractThrottleManager; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013import javax.annotation.Nonnull; 014 015/** 016 * MQTT implementation of a ThrottleManager based on the 017 * AbstractThrottleManager. 018 * 019 * @author Dean Cording Copyright (C) 2023 020 */ 021 022public class MqttThrottleManager extends AbstractThrottleManager { 023 024 protected HashMap<LocoAddress, MqttThrottle> throttles = new HashMap<LocoAddress, MqttThrottle>(5); 025 026 /** 027 * Constructor. 028 * @param memo the memo for the connection this tm will use 029 */ 030 public MqttThrottleManager(MqttSystemConnectionMemo memo) { 031 super(memo); 032 } 033 034 public void setSendThrottleTopic(@Nonnull String sendThrottleTopic) { 035 this.sendThrottleTopic = sendThrottleTopic; 036 } 037 public void setRcvThrottleTopic(@Nonnull String rcvThrottleTopic) { 038 this.rcvThrottleTopic = rcvThrottleTopic; 039 } 040 public void setSendDirectionTopic(@Nonnull String sendDirectionTopic) { 041 this.sendDirectionTopic = sendDirectionTopic; 042 } 043 public void setRcvDirectionTopic(@Nonnull String rcvDirectionTopic) { 044 this.rcvDirectionTopic = rcvDirectionTopic; 045 } 046 public void setSendFunctionTopic(@Nonnull String sendFunctionTopic) { 047 this.sendFunctionTopic = sendFunctionTopic; 048 } 049 public void setRcvFunctionTopic(@Nonnull String rcvFunctionTopic) { 050 this.rcvFunctionTopic = rcvFunctionTopic; 051 } 052 053 @Nonnull 054 public String sendThrottleTopic = "cab/{0}/throttle"; // for constructing topic; public for script access 055 @Nonnull 056 public String rcvThrottleTopic = "cab/{0}/throttle"; // for constructing topic; public for script access 057 @Nonnull 058 public String sendDirectionTopic = "cab/{0}/direction"; // for constructing topic; public for script access 059 @Nonnull 060 public String rcvDirectionTopic = "cab/{0}/direction"; // for constructing topic; public for script access 061 @Nonnull 062 public String sendFunctionTopic = "cab/{0}/function/{1}"; // for constructing topic; public for script access 063 @Nonnull 064 public String rcvFunctionTopic = "cab/{0}/function/{1}"; // for constructing topic; public for script access 065 066 067 /** 068 * Request a new throttle object be created for the address, and let the 069 * throttle listeners know about it. 070 * 071 */ 072 @Override 073 public void requestThrottleSetup(LocoAddress address, boolean control) { 074 MqttThrottle throttle; 075 log.debug("Requesting Throttle: {}", address); 076 if (throttles.containsKey(address)) { 077 notifyThrottleKnown(throttles.get(address), address); 078 } else { 079 throttle = new MqttThrottle((MqttSystemConnectionMemo) adapterMemo, 080 sendThrottleTopic, rcvThrottleTopic, 081 sendDirectionTopic, rcvDirectionTopic, 082 sendFunctionTopic, rcvFunctionTopic, address); 083 throttles.put(address, throttle); 084 notifyThrottleKnown(throttle, address); 085 } 086 } 087 088 /** 089 * MQTT based systems DO use the Dispatch Function 090 */ 091 @Override 092 public boolean hasDispatchFunction() { 093 return true; 094 } 095 096 /** 097 * MQTT based systems can have multiple throttles for the same 098 * device 099 * <p> 100 * {@inheritDoc} 101 */ 102 @Override 103 protected boolean singleUse() { 104 return false; 105 } 106 107 /** 108 * Address 128 and above is a long address 109 * 110 */ 111 @Override 112 public boolean canBeLongAddress(int address) { 113 return isLongAddress(address); 114 } 115 116 /** 117 * Address between 1 and 127 is a short address 118 * 119 */ 120 @Override 121 public boolean canBeShortAddress(int address) { 122 return (address >= 1 && !isLongAddress(address)); 123 } 124 125 /** 126 * There are no ambiguous addresses on this system. 127 */ 128 @Override 129 public boolean addressTypeUnique() { 130 return true; 131 } 132 133 /* 134 * Local method for deciding short/long address 135 * (is it?) 136 */ 137 static protected boolean isLongAddress(int num) { 138 return (num >= 128); 139 } 140 141 /** 142 * What speed modes are supported by this system? value should be xor of 143 * possible modes specifed by the DccThrottle interface DCC++ supports 144 * 14,27,28 and 128 speed step modes 145 */ 146 @Override 147 public EnumSet<SpeedStepMode> supportedSpeedModes() { 148 return EnumSet.of(SpeedStepMode.NMRA_DCC_128); } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override 154 public boolean disposeThrottle(DccThrottle t, ThrottleListener l) { 155 156 log.debug("disposeThrottle {}", t); 157 158 if (super.disposeThrottle(t, l)) { 159 if (t instanceof MqttThrottle) { 160 MqttThrottle lnt = (MqttThrottle) t; 161 throttles.remove(lnt.getLocoAddress()); // remove from throttles map. 162 lnt.throttleDispose(); 163 return true; 164 } 165 } 166 log.error("Dispose Throttle failed {}", t); 167 return false; 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 @Override 174 public void dispatchThrottle(DccThrottle t, ThrottleListener l) { 175 log.debug("dispatchThrottle {}", t); 176 disposeThrottle(t,l); 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override 183 public void releaseThrottle(DccThrottle t, ThrottleListener l) { 184 log.debug("releaseThrottle {}", t); 185 if (t instanceof MqttThrottle) { // should always be the case as it was made that way 186 ((MqttThrottle)t).throttleRelease(); 187 } 188 disposeThrottle(t, l); 189 } 190 191 192 private final static Logger log = LoggerFactory.getLogger(MqttThrottleManager.class); 193 194}