001package jmri.jmrix.loconet.locostats; 002 003import java.util.Vector; 004import javax.annotation.Nonnull; 005 006import jmri.jmrix.loconet.LnConstants; 007import jmri.jmrix.loconet.LocoNetListener; 008import jmri.jmrix.loconet.LocoNetMessage; 009import jmri.jmrix.loconet.LocoNetSystemConnectionMemo; 010 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014/** 015 * Implements functionality to query the LocoNet interface device for status. 016 * 017 * @author Bob Milhaupt Copyright (C) 2017 018 */ 019public class LocoStatsFunc implements LocoNetListener { 020 private LocoNetSystemConnectionMemo memo; 021 public LocoStatsFunc(LocoNetSystemConnectionMemo memo) { 022 this.memo = memo; 023 updatePending = false; 024 need2ndUpdate = false; 025 ifaceStatus = null; 026 if (memo != null) { 027 this.memo.getLnTrafficController().addLocoNetListener(0, this); 028 } 029 } 030 private boolean updatePending; 031 private boolean need2ndUpdate; 032 private Object ifaceStatus; 033 034 /** 035 * Request LocoNet interface Status 036 */ 037 public void sendLocoNetInterfaceStatusQueryMessage() { 038 updatePending = true; 039 need2ndUpdate = false; // assume that we do not need a second query 040 041 log.debug("Sent a LocoNet interface status query"); 042 sendQuery(); 043 } 044 045 private void sendQuery() { 046 if (memo == null) { 047 return; 048 } 049 LocoNetMessage l = new LocoNetMessage(new int[] {0x81, 0x7f}); 050 memo.getLnTrafficController().sendLocoNetMessage(l); 051 } 052 053 /** 054 * LocoNet message handler. 055 * 056 * @param msg incoming LocoNet message to be interpreted 057 */ 058 @Override 059 public void message(LocoNetMessage msg) { 060 if ((msg.getOpCode() == LnConstants.OPC_PEER_XFER) 061 && (msg.getElement(1) == 0x10) 062 && (msg.getElement(2) == 0x50) 063 && (msg.getElement(3) == 0x50) 064 && (msg.getElement(4) == 0x01) 065 && ((msg.getElement(5) & 0xF0) == 0x0) 066 && ((msg.getElement(10) & 0xF0) == 0x0) 067 && updatePending) { 068 // LocoBuffer II form 069 int[] data = msg.getPeerXfrData(); 070 ifaceStatus = new LocoBufferIIStatus( 071 data[0]*256+data[4], 072 (data[5] << 16) + (data[6] << 8) + data[7], 073 (data[1] << 16) + (data[2] << 8) + data[3] 074 ); 075 updatePending = false; 076 updateListeners(); 077 log.debug("Got a LocoNet interface status reply: LocoBufferII"); 078 079 } else if ((msg.getOpCode() == LnConstants.OPC_PEER_XFER) 080 && (msg.getElement(1) == 0x10) 081 && (msg.getElement(2) == 0x22) 082 && (msg.getElement(3) == 0x22) 083 && (msg.getElement(4) == 0x01) 084 && ((msg.getElement(5) & 0x70) == 0x0) 085 && ((msg.getElement(10) & 0x70) == 0x0) 086 && updatePending) { // Digitrax form, check PR2/PR3 or MS100/PR3 mode 087 088 if ((msg.getElement(8) & 0x20) == 0) { 089 // PR2 format 090 int[] data = msg.getPeerXfrData(); 091 ifaceStatus = new PR2Status( 092 data[1] * 256 + data[0], 093 data[2], 094 data[3], 095 data[4], 096 data[5] 097 ); 098 log.debug("Got a LocoNet interface status reply: PR2 mode"); 099 if (updatePending) { 100 if (need2ndUpdate == false) { 101 need2ndUpdate = true; 102 sendQuery(); // get info for MS100 mode, too 103 } else { 104 need2ndUpdate = false; 105 updatePending = false; 106 } 107 } 108 109 } else { 110 // MS100 format 111 int[] data = msg.getPeerXfrData(); 112 ifaceStatus = new PR3MS100ModeStatus( 113 data[1] * 256 + data[0], 114 data[5] * 256 + data[4], 115 data[2] 116 ); 117 log.debug("Got a LocoNet interface status reply: PR3 MS100 mode"); 118 if (updatePending) { 119 if (need2ndUpdate == false) { 120 need2ndUpdate = true; 121 sendQuery(); // get info for PR2 mode, too 122 } else { 123 need2ndUpdate = false; 124 updatePending = false; 125 } 126 } 127 } 128 updateListeners(); 129 130 } else if ((msg.getOpCode() == LnConstants.OPC_PEER_XFER) && 131 (msg.getElement(1) == 0x10) && 132 updatePending) { 133 // Raw mode format 134 // Accept only the first OPC_PEER_XFER of length 0x10 after a request. 135 // This assumes that the interface device will be the first response 136 // after the request, and that the reply will be a "typical" OPC_PEER_XFER 137 // message, and not one of the "alternate" forms with different overall 138 // length.. 139 int[] data = msg.getPeerXfrData(); 140 ifaceStatus = new RawStatus(data[0], data[1], data[2], data[3], 141 data[4], data[5], data[6], data[7] 142 ); 143 144 updatePending = false; 145 updateListeners(); 146 log.debug("Got a LocoNet interface status reply: Raw mode"); 147 } 148 } 149 private void updateListeners() { 150 listeners.stream().forEach((l) -> { 151 l.notifyChangedInterfaceStatus(ifaceStatus); 152 }); 153 } 154 155 /** 156 * Get the latest interface status 157 * 158 * @return the latest interface status; will be null if status has 159 * not been pulled. 160 */ 161 public Object getInterfaceStatus() { 162 return ifaceStatus; 163 } 164 165 /** 166 * Free resources when no longer used 167 */ 168 public void dispose() { 169 listeners.removeAllElements(); 170 listeners = null; 171 } 172 173 // The methods to implement adding and removing listeners 174 protected Vector<LocoNetInterfaceStatsListener> listeners = new Vector<LocoNetInterfaceStatsListener>(); 175 176 /** 177 * Add a listener to the list of listeners which will be notified upon receipt 178 * a LocoNet message containing interface statistics. 179 * 180 * @param l LocoNetInterfaceStatsListener to be added 181 */ 182 public synchronized void addLocoNetInterfaceStatsListener(@Nonnull LocoNetInterfaceStatsListener l) { 183 java.util.Objects.requireNonNull(l); 184 // add only if not already registered 185 if (!listeners.contains(l)) { 186 listeners.addElement(l); 187 } 188 } 189 190 /** 191 * Remove a listener (if present) from the list of listeners which will be 192 * notified upon receipt LocoNet message containing interface statistics. 193 * 194 * @param l LocoNetInterfaceStatsListener to be removed 195 */ 196 public synchronized void removeLocoNetInterfaceStatsListener(@Nonnull LocoNetInterfaceStatsListener l) { 197 java.util.Objects.requireNonNull(l); 198 if (listeners.contains(l)) { 199 listeners.removeElement(l); 200 } 201 } 202 203 private final static Logger log = LoggerFactory.getLogger(LocoStatsFunc.class); 204}