001package jmri.jmrix.loconet; 002 003import jmri.DccLocoAddress; 004import jmri.LocoAddress; 005import jmri.SpeedStepMode; 006import jmri.jmrix.AbstractThrottle; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * An implementation of DccThrottle via AbstractThrottle with code specific to a 012 * PR2 connection. 013 * <p> 014 * Speed in the Throttle interfaces and AbstractThrottle is a float, but in 015 * LocoNet is an int with values from 0 to 127. 016 * 017 * @author Bob Jacobsen Copyright (C) 2006 018 */ 019public class Pr2Throttle extends AbstractThrottle { 020 021 private final int addr; 022 DccLocoAddress address; 023 024 /** 025 * Constructor 026 * @param memo a LocoNetSystemConnectionMemo to associate with this throttle 027 * @param address a DccLocoAddress to associate with this throttle 028 */ 029 public Pr2Throttle(LocoNetSystemConnectionMemo memo, DccLocoAddress address) { 030 super(memo); 031 this.address = address; 032 addr = address.getNumber(); 033 setSpeedStepMode(SpeedStepMode.NMRA_DCC_28); 034 } 035 036 /** 037 * Convert a LocoNet speed integer to a float speed value. 038 * 039 * @param lSpeed loconet speed value 040 * @return speed as float 0->1.0 041 */ 042 protected float floatSpeed(int lSpeed) { 043 if (lSpeed == 0) { 044 return 0.f; 045 } else if (lSpeed == 1) { 046 return -1.f; // estop 047 } 048 if (getSpeedStepMode() == SpeedStepMode.NMRA_DCC_28) { 049 if (lSpeed <= 15) //Value less than 15 is in the stop/estop range bracket 050 { 051 return 0.f; 052 } 053 return (((lSpeed - 12) / 4f) / 28.f); 054 } else if (getSpeedStepMode() == SpeedStepMode.NMRA_DCC_14) { 055 if (lSpeed <= 15) //Value less than 15 is in the stop/estop range bracket 056 { 057 return 0.f; 058 } 059 return ((lSpeed - 8) / 8f) / 14.f; 060 } else { 061 return ((lSpeed - 1) / 126.f); 062 } 063 } 064 065 /** 066 * {@inheritDoc} 067 * <p> 068 * This implementation does not support 128 speed steps. 069 */ 070 @Override 071 // This is a specific implementation for the PR2 that seems to 072 // return different values from the super class. This is an attempt at only 073 // outputting those speeds to the Pr2 that generate discrete DCC speeds 074 // 14/28 speed steps are the same as LocoNetThrottle.intSpeed 075 protected int intSpeed(float fSpeed) { 076 int speed; 077 if (fSpeed < 0.) { 078 return 1; 079 } // emergency stop 080 if (fSpeed == 0.) { 081 return 0; 082 } // idle (zero speed) 083 084 switch (this.getSpeedStepMode()) { 085 case NMRA_DCC_28: 086 case MOTOROLA_28: 087 speed = (int) ((fSpeed * 28) * 4) + 12; 088 // ensure we never send a non-zero speed to loconet 089 // that we reinterpret as 0 in floatSpeed() later 090 if (speed < 16) { 091 speed = 16; 092 } 093 return speed; 094 095 case NMRA_DCC_14: 096 speed = (int) ((fSpeed * 14) * 8) + 8; 097 // ensure we never send a non-zero speed to loconet 098 // that we reinterpret as 0 in floatSpeed() later 099 if (speed < 16) { 100 speed = 16; 101 } 102 return speed; 103 104 default: 105 // includes the 128 case 106 log.warn("Unhandled speed step mode: {}", this.getSpeedStepMode()); 107 return super.intSpeed(fSpeed); 108 } 109 } 110 111 public void writeData() { 112 // convert contents 113 int stat = 0; 114 int speed; 115 synchronized(this) { 116 speed = intSpeed(speedSetting); 117 } 118 int dirf = 0; // contains dir, f0, f4-1 119 if (getFunction(0)) { 120 dirf |= (1 << 4); 121 } 122 if (getFunction(1)) { 123 dirf |= (1 << 0); 124 } 125 if (getFunction(2)) { 126 dirf |= (1 << 1); 127 } 128 if (getFunction(3)) { 129 dirf |= (1 << 2); 130 } 131 if (getFunction(4)) { 132 dirf |= (1 << 3); 133 } 134 if (!getIsForward()) { 135 dirf |= (1 << 5); // note sign of bit 136 } 137 // contains f11-5 138 int f11 = 0; 139 if (getFunction(5)) { 140 f11 |= (1 << 0); 141 } 142 if (getFunction(6)) { 143 f11 |= (1 << 1); 144 } 145 if (getFunction(7)) { 146 f11 |= (1 << 2); 147 } 148 if (getFunction(8)) { 149 f11 |= (1 << 3); 150 } 151 if (getFunction(9)) { 152 f11 |= (1 << 4); 153 } 154 if (getFunction(10)) { 155 f11 |= (1 << 5); 156 } 157 if (getFunction(11)) { 158 f11 |= (1 << 6); 159 } 160 161 // contains F19-F13 162 int f19 = 0; 163 164 // contains F27-F21 165 int f27 = 0; 166 167 // contains F28, F20, F12 168 int f28 = 0; 169 if (getFunction(12)) { 170 f28 |= (1 << 4); 171 } 172 173 LocoNetMessage l = new LocoNetMessage(21); 174 l.setOpCode(LnConstants.OPC_EXP_WR_SL_DATA); 175 int i = 1; 176 l.setElement(i++, 21); // length 177 l.setElement(i++, 0); // EXP_MAST 178 l.setElement(i++, 1); // EXP_SLOT 179 l.setElement(i++, stat & 0x7F); // EXPD_STAT 180 l.setElement(i++, addr & 0x7F); // EXPD_ADRL 181 l.setElement(i++, (addr / 128) & 0x7F); // EXPD_ADRH 182 l.setElement(i++, 0); // EXPD_FLAGS 183 l.setElement(i++, speed & 0x7F); // EXPD_SPD 184 l.setElement(i++, f28 & 0x7F); // EXPD_F28F20F12 185 l.setElement(i++, dirf & 0x7F); // EXPD_DIR_F0F4_F1 186 l.setElement(i++, f11 & 0x7F); // EXPD_F11_F5 187 l.setElement(i++, f19 & 0x7F); // EXPD_F19_F13 188 l.setElement(i++, f27 & 0x7F); // EXPD_F27_F21 189 // rest are zero 190 191 ((LocoNetSystemConnectionMemo) adapterMemo).getLnTrafficController().sendLocoNetMessage(l); 192 } 193 194 /** 195 * Send the LocoNet message to set the state of locomotive direction and 196 * functions F0, F1, F2, F3, F4. Invoked by AbstractThrottle when needed. 197 */ 198 @Override 199 protected void sendFunctionGroup1() { 200 writeData(); 201 } 202 203 /** 204 * Send the LocoNet message to set the state of functions F5, F6, F7, F8. 205 * Invoked by AbstractThrottle when needed. 206 */ 207 @Override 208 protected void sendFunctionGroup2() { 209 writeData(); 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override 216 protected void sendFunctionGroup3() { 217 writeData(); 218 } 219 220 /** 221 * Set the speed. 222 * <p> 223 * This intentionally skips the emergency stop value of 1. 224 * 225 * @param speed Number from 0 to 1; less than zero is emergency stop 226 */ 227 @Override 228 public synchronized void setSpeedSetting(float speed) { 229 float oldSpeed = this.speedSetting; 230 this.speedSetting = speed; 231 if (speed < 0) { 232 this.speedSetting = -1.f; 233 } 234 235 writeData(); 236 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); // NOI18N 237 record(speed); 238 } 239 240 /** 241 * LocoNet actually puts forward and backward in the same message as the 242 * first function group. 243 */ 244 @Override 245 public void setIsForward(boolean forward) { 246 boolean old = isForward; 247 isForward = forward; 248 sendFunctionGroup1(); 249 firePropertyChange(ISFORWARD, old, isForward); // NOI18N 250 } 251 252 /** 253 * {@inheritDoc} 254 */ 255 @Override 256 public String toString() { 257 return getLocoAddress().toString(); 258 } 259 260 /** 261 * {@inheritDoc} 262 */ 263 @Override 264 public LocoAddress getLocoAddress() { 265 return address; 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public void throttleDispose() { 273 finishRecord(); 274 } 275 276 // initialize logging 277 private final static Logger log = LoggerFactory.getLogger(Pr2Throttle.class); 278 279}