001package jmri.jmrix.loconet.locoio; 002 003import java.util.Vector; 004import jmri.jmrix.loconet.LnConstants; 005 006/** 007 * Manage the set of valid modes for a particular LocoIO port, 008 * as well as the conversions between addresses and SV values. 009 * Used in LocoIO tool. 010 * Marked Legacy/Deprecated since 2017 version 4.12. 011 * @author John Plocher, January 30, 2007 012 */ 013public class LocoIOModeList { 014 015 protected Vector<LocoIOMode> modeList = new Vector<LocoIOMode>(); 016 protected String[] validmodes; 017 018 /** 019 * Create a new instance of LocoIOModeList 020 */ 021 public LocoIOModeList() { 022 023 /* 024 * Initialize various configuration modes. 025 * @TODO: Need to tag these with which firmware rev supports 026 * them and only allow choices that match. 027 * 028 * Inputs... 029 */ 030 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x0F, 0x00, "Toggle Switch, LocoIO 1.3.2")); 031 032 modeList.add(new LocoIOMode(0, LnConstants.OPC_INPUT_REP, 0x5F, 0x00, "Block Detector, Active High")); 033 modeList.add(new LocoIOMode(0, LnConstants.OPC_INPUT_REP, 0x1F, 0x10, "Block Detector, Active Low")); 034 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x0F, 0x10, "Toggle Switch, Direct Control")); 035 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x07, 0x10, "Toggle Switch, Indirect Control")); 036 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x6F, 0x00, "Push Button, Active High, Direct Control")); 037 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x67, 0x00, "Push Button, Active High, Indirect Control")); 038 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x2F, 0x10, "Push Button, Active Low, Direct Control")); 039 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x27, 0x10, "Push Button, Active Low, Indirect Control")); 040 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x17, 0x70, "Turnout Feedback, single sensor")); 041 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x37, 0x70, "Turnout Feedback, dual sensor, #1")); 042 modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x37, 0x60, "Turnout Feedback, dual sensor, #2")); 043 /* 044 * and Outputs... 045 */ 046 modeList.add(new LocoIOMode(1, LnConstants.OPC_INPUT_REP, 0xC0, 0x00, "Block Occupied Indication")); 047 modeList.add(new LocoIOMode(1, LnConstants.OPC_INPUT_REP, 0xD0, 0x00, "Block Occupied Indication, Blinking")); 048 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x81, 0x10, "Steady State, single output, On at Power up")); 049 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x80, 0x10, "Steady State, single output, Off at Power up")); 050 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x81, 0x30, "Steady State, paired output, On at Power up")); 051 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x80, 0x30, "Steady State, paired output, Off at Power up")); 052 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x91, 0x10, "Steady State, single output, On at Power up, Blinking")); 053 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x90, 0x10, "Steady State, single output, Off at Power up, Blinking")); 054 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x91, 0x30, "Steady State, paired output, On at Power up, Blinking")); 055 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x90, 0x30, "Steady State, paired output, Off at Power up, Blinking")); 056 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x88, 0x20, "Pulsed, software controlled on time, single output")); 057 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x8C, 0x20, "Pulsed, firmware controlled on time, single output")); 058 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x88, 0x00, "Pulsed, software controlled on time, paired output")); 059 modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x8C, 0x00, "Pulsed, firmware controlled on time, paired output")); 060 061 validmodes = new String[modeList.size()]; 062 for (int i = 0; i <= modeList.size() - 1; i++) { 063 LocoIOMode m = modeList.elementAt(i); 064 validmodes[i] = m.getFullMode(); 065 } 066 } 067 068 protected String[] getValidModes() { 069 return validmodes; 070 } 071 072 protected boolean isValidModeValue(Object value) { 073 if (value instanceof String) { 074 String sValue = (String) value; 075 for (int i = 0; i < validmodes.length; i++) { 076 if (sValue.equals(validmodes[i])) { 077 return true; 078 } 079 } 080 } 081 return false; 082 } 083 084 protected LocoIOMode getLocoIOModeFor(String s) { 085 for (int i = 0; i <= modeList.size() - 1; i++) { 086 LocoIOMode m = modeList.elementAt(i); 087 String ms = m.getFullMode(); 088 if (ms.matches(s)) { 089 return m; 090 } 091 } 092 return null; 093 } 094 095 protected LocoIOMode getLocoIOModeFor(int cv, int v1, int v2) { 096 // v2 &= 0x0F; 097 for (int i = 0; i <= modeList.size() - 1; i++) { 098 LocoIOMode m = modeList.elementAt(i); 099 if (m.getSV() == cv) { 100 if ((m.getOpCode() == LnConstants.OPC_INPUT_REP) 101 && (m.getV2() == (v2 & 0xD0))) { 102 return m; 103 } else if (((cv == 0x6F) || (cv == 0x67) || (cv == 0x2F) || (cv == 0x27)) 104 && (m.getV2() == (v2 & 0x50))) { 105 return m; 106 } else if ((m.getV2() == (v2 & 0xB0))) { 107 return m; 108 } else if (((cv & 0x90) == 0x10) 109 && ((cv & 0x80) == 0) 110 && (m.getV2() == (v2 & 0x70))) { 111 return m; 112 } 113 } 114 } 115 return null; 116 } 117 118 /** 119 * Convert Value1 (Low bits) from Port Address. 120 * 121 * @param lim one of a list of defined port operation modes 122 * @param address the address for this port 123 * @return low-bits value 124 */ 125 protected int addressToValue1(LocoIOMode lim, int address) { 126 if (lim == null) { 127 return 0; 128 } 129 return addressToValues(lim.getOpCode(), lim.getSV(), lim.getV2(), address) & 0x7F; 130 } 131 132 /** 133 * Convert Value2 (High bits) from Port Address. 134 * 135 * @param lim one of a list of defined port operation modes 136 * @param address the address for this port 137 * @return high-bits value 138 */ 139 protected int addressToValue2(LocoIOMode lim, int address) { 140 if (lim == null) { 141 return 0; 142 } 143 return (addressToValues(lim.getOpCode(), lim.getSV(), lim.getV2(), address) / 256) & 0x7F; 144 } 145 146 /** 147 * Convert bytes from LocoNet packet into a 1-based address for a sensor or 148 * turnout. 149 * 150 * @param a1 Byte containing the upper bits 151 * @param a2 Byte containing the lower bits 152 * @return 1-4096 address as decimal 153 */ 154 static private int SENSOR_ADR(int a1, int a2) { 155 return (((a2 & 0x0f) * 128) + (a1 & 0x7f)) + 1; 156 } 157 158 /** 159 * Create 2 byte value from Port Address bits. 160 * 161 * @param opcode coded value for message type 162 * @param sv index of SV value to create, ignored 163 * @param v2mask mask to apply on Value2 164 * @param address the address for this port 165 * @return 2-byte value 166 */ 167 protected int addressToValues(int opcode, int sv, int v2mask, int address) { 168 int v1 = 0; 169 int v2 = 0; 170 171 address--; 172 173 if (opcode == LnConstants.OPC_INPUT_REP) { 174 v1 = ((address / 2) & 0x7F); 175 v2 = ((address / 256) & 0x0F); 176 if ((address & 0x01) == 0x01) { 177 v2 |= LnConstants.OPC_INPUT_REP_SW; 178 } 179 v2 |= v2mask; 180 } else if (opcode == LnConstants.OPC_SW_REQ) { 181 v1 = (address & 0x7F); 182 v2 = (address / 128) & 0x0F; 183 v2 &= ~(0x40); 184 v2 |= v2mask; 185 } else if (opcode == LnConstants.OPC_SW_REP) { 186 v1 = (address & 0x7F); 187 v2 = (address / 128) & 0x0F; 188 v2 &= ~(0x40); 189 v2 |= v2mask; 190 } 191 return v2 * 256 + v1; 192 } 193 194 /** 195 * Extract Port Address from the 3 SV values. 196 * 197 * @param opcode coded value for message type 198 * @param sv first SV value, ignored 199 * @param v1 second value (upper bits) 200 * @param v2 second value (lower bits) 201 * @return address (int) of the port 202 */ 203 protected int valuesToAddress(int opcode, int sv, int v1, int v2) { 204 //int hi = 0; 205 //int lo = 0; 206 if (opcode == LnConstants.OPC_INPUT_REP) { /* return 1-4096 address */ 207 208 return ((SENSOR_ADR(v1, v2) - 1) * 2 + ((v2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1)); 209 } else if (opcode == LnConstants.OPC_SW_REQ) { 210 // if ( ((v2 & 0xCF) == 0x0F) && ((v1 & 0xFC) == 0x78) ) { // broadcast address LPU V1.0 page 12 211 // "Request Switch to broadcast address with bits "+ 212 // "a="+ ((sw2&0x20)>>5)+((sw2 & LnConstants.OPC_SW_REQ_DIR)!=0 ? " (Closed)" : " (Thrown)")+ 213 // " c="+ ((sw1 & 0x02)>>1) + 214 // " b="+ ((sw1 & 0x01)) + 215 // "\n\tOutput "+ 216 // ((sw2 & LnConstants.OPC_SW_REQ_OUT)!=0 ? "On" : "Off")+"\n"; 217 // } else if ( ((v2 & 0xCF) == 0x07) && ((v1 & 0xFC) == 0x78) ) { // broadcast address LPU V1.0 page 13 218 // "Request switch command is Interrogate LocoNet with bits "+ 219 // "a="+ ((sw2 & 0x20)>>5) + 220 // " c="+ ((sw1&0x02)>>1) + 221 // " b="+ ((sw1&0x01)) + 222 // "\n\tOutput "+ 223 // ((sw2 & LnConstants.OPC_SW_REQ_OUT)!=0 ? "On" : "Off")+"\n"+ 224 // ( ( (sw2&0x10) == 0 ) ? "" : "\tNote 0x10 bit in sw2 is unexpectedly 0\n"); 225 // } else { // normal command 226 return (SENSOR_ADR(v1, v2)); 227 //} 228 } else if (opcode == LnConstants.OPC_SW_REP) { 229 return (SENSOR_ADR(v1, v2)); 230 } 231 return -1; 232 } 233 234 protected int valuesToAddress(LocoIOMode lim, int sv, int v1, int v2) { 235 if (lim == null) { 236 return 0; 237 } 238 return valuesToAddress(lim.getOpCode(), sv, v1, v2); 239 } 240 241 // private final static Logger log = LoggerFactory.getLogger(LocoIOModeList.class); 242 243}