001package jmri.jmrix.tmcc; 002 003import jmri.DccLocoAddress; 004import jmri.LocoAddress; 005import jmri.SpeedStepMode; 006import jmri.jmrix.AbstractThrottle; 007 008/** 009 * An implementation of DccThrottle. 010 * <p> 011 * Addresses of 99 and below are considered short addresses, and over 100 are 012 * considered long addresses. 013 * 014 * @author Bob Jacobsen Copyright (C) 2001, 2006 015 */ 016public class SerialThrottle extends AbstractThrottle { 017 018 /** 019 * Constructor. 020 * 021 * @param memo the connected SerialTrafficController 022 * @param address Loco ID 023 */ 024 public SerialThrottle(TmccSystemConnectionMemo memo, DccLocoAddress address) { 025 super(memo, 69); // supports 69 functions 026 tc = memo.getTrafficController(); 027 028 // cache settings. It would be better to read the 029 // actual state, but I don't know how to do this 030 synchronized(this) { 031 this.speedSetting = 0; 032 } 033 // Functions default to false 034 this.address = address; 035 this.isForward = true; 036 this.speedStepMode = SpeedStepMode.TMCC_32; 037 } 038 039 private final DccLocoAddress address; 040 private final SerialTrafficController tc; 041 042 /** 043 * {@inheritDoc} 044 */ 045 @Override 046 public LocoAddress getLocoAddress() { 047 return address; 048 } 049 050 /** 051 * {@inheritDoc} 052 */ 053 @Override 054 public void setFunction(int func, boolean value) { 055 updateFunction(func, value); 056 if (func>=0 && func < SERIAL_FUNCTION_CODES.length) { 057 if ( SERIAL_FUNCTION_CODES[func] > 0xFFFF ) { 058 // TMCC 2 format 059 sendToLayout(SERIAL_FUNCTION_CODES[func] + address.getNumber() * 512); 060 } else { 061 // TMCC 1 format 062 sendToLayout(SERIAL_FUNCTION_CODES[func] + address.getNumber() * 128); 063 } 064 } 065 else { 066 super.setFunction(func, value); 067 } 068 } 069 070 // Translate function number to line characters. 071 // If the upper byte is zero, it will be replaces by 0xF8 072 // and the address will be set in the low position. 073 // If the upper byte is non-zero, that value will be sent, 074 // and the address will be set in the upper (TMCC2) position. 075 private final static int[] SERIAL_FUNCTION_CODES = new int[] { 076 0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */ 077 0x000010, 0x000011, 0x000012, 0x000013, 0x000014, /* Fn5-9 */ 078 0x000015, 0x000016, 0x000017, 0x000018, 0x000019, /* Fn10-14 */ 079 0x000009, 0x00001E, 0x000000, 0x000003, 0x000001, /* Fn15-19 */ 080 0x000004, 0x000007, 0x000047, 0x000042, 0x000028, /* Fn20-24 */ 081 0x000029, 0x00002A, 0x00002B, /* 25-27 */ 082 // start of TMCC 2 functions 083 0xF801FB, // Fn28 Start Up Sequence 1 (Delayed Prime Mover) 084 0xF801FC, // Fn29 Start Up Sequence 2 (Immediate Start Up) 085 0xF801FD, // Fn30 Shut Down Sequence 1 (Delay w/ Announcement) 086 0xF801FE, // Fn31 Shut down Sequence 2 (Immediate Shut Down) 087 0xF90000, // Fn32 088 0xF90000, // Fn33 089 0xF90000, // Fn34 090 0xF90000, // Fn35 091 0xF90000, // Fn36 092 0xF90000, // Fn37 093 0xF90000, // Fn38 094 0xF90000, // Fn39 095 0xF90000, // Fn40 096 0xF90000, // Fn41 097 0xF90000, // Fn42 098 0xF90000, // Fn43 099 0xF90000, // Fn44 100 0xF90000, // Fn45 101 0xF90000, // Fn46 102 0xF90000, // Fn47 103 0xF90000, // Fn48 104 0xF90000, // Fn49 105 0xF90000, // Fn50 106 0xF90000, // Fn51 107 0xF90000, // Fn52 108 0xF90000, // Fn53 109 0xF90000, // Fn54 110 0xF90000, // Fn55 111 0xF90000, // Fn56 112 0xF90000, // Fn57 113 0xF90000, // Fn58 114 0xF90000, // Fn59 115 0xF90000, // Fn60 116 0xF90000, // Fn61 117 0xF90000, // Fn62 118 0xF90000, // Fn63 119 0xF90000, // Fn64 120 0xF90000, // Fn65 121 0xF90000, // Fn66 122 0xF90000, // Fn67 123 }; 124 125 /** 126 * Set the speed. 127 * 128 * @param speed Number from 0 to 1; less than zero is emergency stop 129 */ 130 @Override 131 public void setSpeedSetting(float speed) { 132 float oldSpeed; 133 synchronized(this) { 134 oldSpeed = this.speedSetting; 135 this.speedSetting = speed; 136 } 137 int value = (int) (32 * speed); // -1 for rescale to avoid estop 138 if (value > 31) { 139 value = 31; // max possible speed 140 } 141 SerialMessage m = new SerialMessage(); 142 143 if (value < 0) { 144 // immediate stop 145 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 146 } else { 147 // normal speed setting 148 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 149 } 150 151 tc.sendSerialMessage(m, null); 152 tc.sendSerialMessage(m, null); 153 tc.sendSerialMessage(m, null); 154 tc.sendSerialMessage(m, null); 155 synchronized(this) { 156 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 157 } 158 record(speed); 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 public void setIsForward(boolean forward) { 166 boolean old = isForward; 167 isForward = forward; 168 169 // notify layout 170 SerialMessage m = new SerialMessage(); 171 if (forward) { 172 m.putAsWord(0x0000 + address.getNumber() * 128); 173 } else { 174 m.putAsWord(0x0003 + address.getNumber() * 128); 175 } 176 tc.sendSerialMessage(m, null); 177 tc.sendSerialMessage(m, null); 178 tc.sendSerialMessage(m, null); 179 tc.sendSerialMessage(m, null); 180 firePropertyChange(ISFORWARD, old, isForward); 181 } 182 183 /** 184 * Send these messages to the layout four times 185 * to make sure they're accepted. 186 * @param value Content of message to be sent in three bytes 187 */ 188 protected void sendToLayout(int value) { 189 tc.sendSerialMessage(new SerialMessage(value), null); 190 tc.sendSerialMessage(new SerialMessage(value), null); 191 tc.sendSerialMessage(new SerialMessage(value), null); 192 tc.sendSerialMessage(new SerialMessage(value), null); 193 } 194 195 /* 196 * Set the speed step value. 197 * <p> 198 * Only 32 steps is available 199 * 200 * @param Mode ignored, as only 32 is valid 201 */ 202 @Override 203 public void setSpeedStepMode(jmri.SpeedStepMode Mode) { 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public void throttleDispose() { 211 finishRecord(); 212 } 213 214}