001package jmri.jmrix.maple; 002 003import jmri.JmriException; 004import jmri.Sensor; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Utility Class supporting input from Maple HMI's 010 * <p> 011 * All of the Maple HMI panels are configured with the same input bits. As each 012 * HMI is polled, the results are ORed together in an input array that is 013 * initialized to all 0 when a polling cycle is initiated. That way, if a bit is 014 * 1 in any of the HMI's, it will be 1 in the input array at the end of the 015 * polling cycle. At the end of each polling cycle, each input bit is compared 016 * to the input bit from the last polling cycle. If a bit has changed, or if 017 * this is the first polling cycle, the correspnding Sensor state is updated. No 018 * updating occurs if all panels timed out. Serial systems with unique input 019 * bits for each node keep their input array in each node. That code has been 020 * moved to this utility class for Maple Systems because all nodes share the 021 * same set of input bits. Coil bits within Maple Systems HMI's are divided into 022 * input (1-1000) and output (1001-9000), so input bits are read starting from 023 * HMI address 1, and output bits are written starting at HMI address 1001. 024 * 025 * @author Dave Duchamp, Copyright (C) 2009 026 */ 027public class InputBits { 028 029 private SerialTrafficController tc = null; 030 031 public InputBits(SerialTrafficController _tc) { 032 // clear the Sensor arrays 033 for (int i = 0; i < MAXSENSORS + 1; i++) { 034 sensorArray[i] = null; 035 sensorLastSetting[i] = Sensor.UNKNOWN; 036 sensorTempSetting[i] = Sensor.UNKNOWN; 037 sensorORedSetting[i] = false; 038 } 039 tc = _tc; 040 } 041 042 // class constants 043 static final int MAXSENSORS = 1000; 044 045 // operational variables 046 private static int mNumInputBits = 48; // number of Sensors that are configured 047 private static int mTimeoutTime = 2000; // timeout time when polling nodes (milliseconds) 048 private int lastUsedSensor = -1; // number of Sensors that are in use - 1 (less than above) 049 protected Sensor[] sensorArray = new Sensor[MAXSENSORS + 1]; 050 protected int[] sensorLastSetting = new int[MAXSENSORS + 1]; 051 protected int[] sensorTempSetting = new int[MAXSENSORS + 1]; 052 protected boolean[] sensorORedSetting = new boolean[MAXSENSORS + 1]; 053 054 // access routines 055 public static void setNumInputBits(int n) { 056 mNumInputBits = n; 057 } 058 059 public static int getNumInputBits() { 060 return mNumInputBits; 061 } 062 063 public static void setTimeoutTime(int n) { 064 mTimeoutTime = n; 065 } 066 067 public static int getTimeoutTime() { 068 return mTimeoutTime; 069 } 070 071 public int getLastSensor() { 072 return lastUsedSensor; 073 } 074 075 public int getMaxSensors() { 076 return MAXSENSORS; 077 } 078 079 public void forceSensorsUnknown() { 080 // force sensors to UNKNOWN, including callbacks; might take some time 081 for (int i = 0; i <= lastUsedSensor; i++) { 082 if (sensorArray[i] != null) { 083 sensorLastSetting[i] = Sensor.UNKNOWN; 084 sensorTempSetting[i] = Sensor.UNKNOWN; 085 sensorORedSetting[i] = false; 086 try { 087 sensorArray[i].setKnownState(Sensor.UNKNOWN); 088 } catch (jmri.JmriException ex) { 089 log.error("unexpected exception setting sensor i={}", i, ex); 090 } 091 } 092 } 093 } 094 095 /** 096 * Use the contents of the poll reply to mark changes Bits from all the 097 * polled panels are ORed together. So if any panel has the bit on (after 098 * any inversion is provided for), the resulting bit will be on when the 099 * polling cycle is complete. 100 * 101 * @param l Reply to a poll operation 102 */ 103 public void markChanges(SerialReply l) { 104 int begAddress = tc.getSavedPollAddress(); 105 int count = l.getNumDataElements() - 8; 106 for (int i = 0; i < count; i++) { 107 if (sensorArray[i + begAddress - 1] == null) { 108 continue; // skip ones that don't exist 109 } 110 boolean value = ((l.getElement(5 + i) & 0x01) != 0) ^ sensorArray[i + begAddress - 1].getInverted(); 111 112 if (value) { 113 // considered ACTIVE 114 sensorORedSetting[i + begAddress - 1] = true; 115 } 116 } 117 } 118 119 /** 120 * This routine is invoked at the end of each polling cycle to change those 121 * Sensors that have changed during the polling cycle. After making whatever 122 * changes are warranted, this routine clears the array for accumulating a 123 * new polling cycle. Only sensors that are actually defined are updated 124 */ 125 public void makeChanges() { 126 // update Sensors according to poll results 127 try { 128 for (int i = 0; i <= lastUsedSensor; i++) { 129 if (sensorArray[i] == null) { 130 continue; // skip ones that don't exist 131 } 132 boolean value = sensorORedSetting[i]; 133 134 if (value) { 135 // considered ACTIVE 136 if (((sensorTempSetting[i] == Sensor.ACTIVE) 137 || (sensorTempSetting[i] == Sensor.UNKNOWN)) 138 && (sensorLastSetting[i] != Sensor.ACTIVE)) { // see comment at top; allows persistent local changes 139 sensorLastSetting[i] = Sensor.ACTIVE; 140 sensorArray[i].setKnownState(Sensor.ACTIVE); 141 // log.debug("set active"); 142 } 143 // save for next time 144 sensorTempSetting[i] = Sensor.ACTIVE; 145 } else { 146 // considered INACTIVE 147 if (((sensorTempSetting[i] == Sensor.INACTIVE) 148 || (sensorTempSetting[i] == Sensor.UNKNOWN)) 149 && (sensorLastSetting[i] != Sensor.INACTIVE)) { // see comment at top; allows persistent local changes 150 sensorLastSetting[i] = Sensor.INACTIVE; 151 sensorArray[i].setKnownState(Sensor.INACTIVE); 152 // log.debug("set inactive"); 153 } 154 // save for next time 155 sensorTempSetting[i] = Sensor.INACTIVE; 156 } 157 } 158 } catch (JmriException e) { 159 log.error("exception in makeChanges", e); 160 } 161 162 // clear the accumulation array; 163 for (int i = 0; i < lastUsedSensor; i++) { 164 sensorORedSetting[i] = false; 165 } 166 } 167 168 /** 169 * The numbers here are 0 to MAXSENSORS, not 1 to MAXSENSORS. 170 * 171 * @param s Sensor object 172 * @param i 0 to MAXSENSORS number of sensor's input bit on this node 173 */ 174 public void registerSensor(Sensor s, int i) { 175 // validate the sensor ordinal 176 if ((i < 0) || (i >= mNumInputBits)) { 177 log.error("Unexpected sensor ordinal in registerSensor: {}", Integer.toString(i + 1)); 178 return; 179 } 180 if (sensorArray[i] == null) { 181 sensorArray[i] = s; 182 if (lastUsedSensor < i) { 183 lastUsedSensor = i; 184 } 185 sensorLastSetting[i] = Sensor.UNKNOWN; 186 sensorTempSetting[i] = Sensor.UNKNOWN; 187 sensorORedSetting[i] = false; 188 } 189 } 190 191 private final static Logger log = LoggerFactory.getLogger(InputBits.class); 192 193}