001package jmri.jmrix.can.cbus.node; 002 003import javax.annotation.Nonnull; 004import jmri.jmrix.can.CanSystemConnectionMemo; 005import jmri.jmrix.can.cbus.CbusPreferences; 006 007// import org.slf4j.Logger; 008// import org.slf4j.LoggerFactory; 009 010/** 011 * Table data model for display of CBUS Nodes 012 * 013 * @author Steve Young (c) 2019 014 * 015 */ 016public class CbusBasicNodeTableFetch extends CbusBasicNodeTableOperations { 017 018 protected CbusNodeTrickleFetch trickleFetch; 019 020 public CbusBasicNodeTableFetch(@Nonnull CanSystemConnectionMemo memo, int row, int column) { 021 super(memo,row,column); 022 } 023 024 private int urgentNode = -1; 025 private int nodebefore = -1; 026 private int nodeafter = -1; 027 private boolean urgentActive=false; // feature active, set false for background fetch 028 029 /** 030 * Notify the table that the Node data fetch is more urgent 031 */ 032 public void startUrgentFetch() { 033 urgentActive = true; 034 startBackgroundFetch(); 035 } 036 037 /** 038 * Notify the table that the Node data fetch is more urgent 039 * @param nodeNum the Node to prioritise in the fetch 040 */ 041 protected void setUrgentNode( int nodeNum ){ 042 urgentNode = nodeNum; 043 urgentActive = true; 044 startBackgroundFetch(); 045 } 046 047 /** 048 * Fetch data in order of priority based on what user is currently viewing 049 * @param nodenum number of Node to prioritise in the fetch 050 * @param urgentNodeBefore number of the Node in main table row above 051 * @param urgentNodeAfter number of the Node in main table row below 052 */ 053 public void setUrgentFetch(int nodenum, int urgentNodeBefore, int urgentNodeAfter){ 054 urgentNode = nodenum; 055 nodebefore = urgentNodeBefore; 056 nodeafter = urgentNodeAfter; 057 urgentActive = true; 058 startBackgroundFetch(); 059 } 060 061 /** 062 * Request the table send the next urgent fetch 063 */ 064 public void triggerUrgentFetch(){ 065 if (urgentActive) { 066 sendNextBackgroundFetch(); 067 } 068 } 069 070 /** 071 * Starts background fetching for all table data as per user prefs 072 * Call whenever a node has been added to table or node edited 073 */ 074 public void startBackgroundFetch(){ 075 if (!(this instanceof CbusNodeTableDataModel)){ 076 return; 077 } 078 CbusPreferences pref = ((CbusNodeTableDataModel)this).preferences; 079 // reset if already running 080 if ( trickleFetch != null ){ 081 trickleFetch.dispose(); 082 } 083 trickleFetch = null; 084 if ( ( pref != null ) && (pref.getNodeBackgroundFetchDelay() > 0 ) ) { 085 trickleFetch = new CbusNodeTrickleFetch( _memo, this, pref.getNodeBackgroundFetchDelay() ); 086 } 087 } 088 089 private boolean hasActiveTimers(){ 090 for (int i = 0; i < getRowCount(); i++) { 091 // _mainArray.get(i).startLoadFromXml(); 092 _mainArray.get(i).getNodeBackupManager().doLoad(); 093 if ( _mainArray.get(i).getNodeTimerManager().hasActiveTimers() ){ 094 return true; 095 } 096 _mainArray.get(i).getNodeStats().checkNodeFinishedLoad(); 097 } 098 return false; 099 } 100 101 // prioritise command station 102 // get parameters 103 // get node variables 1-16 104 private boolean sentCommandStationFetch(){ 105 for (int i = 0; i < getRowCount(); i++) { 106 if ( _mainArray.get(i).getCsNum() > -1 ) { // is a command station 107 if ( sentOutstandingParam(_mainArray.get(i)) ) { // this param 108 return true; 109 } 110 if (( _mainArray.get(i).getNodeParamManager().getParameter(6) > 15 ) // If CS does has have more than 15 NV's 111 && ( _mainArray.get(i).getNodeNvManager().getNV(16) < 0 )) { 112 _mainArray.get(i).getNodeNvManager().sendNextNVToFetch(); 113 return true; 114 } 115 } 116 } 117 return false; 118 } 119 120 private boolean sentAnAllNodeParam(){ 121 // Get all node parameters fetched so basic node details ( type, num nv's etc. ) are known 122 for (int i = 0; i < getRowCount(); i++) { 123 if ( sentOutstandingParam(_mainArray.get(i)) ) { // this param 124 return true; 125 } 126 } 127 return false; 128 } 129 130 private boolean sentOutstandingNvs(CbusNode thisnode){ 131 if ( thisnode!=null && thisnode.getNodeNvManager().getOutstandingNvCount() > 0 ){ // this nv 132 thisnode.getNodeNvManager().sendNextNVToFetch(); 133 return true; 134 } 135 return false; 136 } 137 138 private boolean sentOutstandingEvs(CbusNode thisnode) { 139 if ( thisnode == null ) { 140 return false; 141 } 142 if ( thisnode.getNodeEventManager().getOutstandingEvVars() > 0 ) { // this events 143 thisnode.getNodeEventManager().sendNextEvVarToFetch(); 144 return true; 145 } 146 return sentOutstandingEvTotal(thisnode); 147 } 148 149 /** 150 * Re-request the total number of events from a node where the events are unknown, 151 * eg. invalidated following a fetch failure or an invalid event index, 152 * but the parameters are already complete so the total would not otherwise 153 * be requested again as part of the parameter fetch. 154 * <p> 155 * Not sent if the node has abandoned an event fetch following repeated timeouts, 156 * the node timers or a node reset will clear this. 157 * @param thisnode the node to check 158 * @return true if a request was sent, else false 159 */ 160 private boolean sentOutstandingEvTotal(CbusNode thisnode) { 161 if ( thisnode.getNodeEventManager().getTotalNodeEvents() < 0 162 && thisnode.getNodeParamManager().getOutstandingParams() == 0 163 && thisnode.getNodeParamManager().getParameter(CbusNodeConstants.EV_PER_EN_IDX) > -1 164 && !thisnode.getNodeTimerManager().eventFetchAbandoned() ) { 165 thisnode.getNodeParamManager().requestEventTot(); 166 return true; 167 } 168 return false; 169 } 170 171 private boolean sentOutstandingNvsOrEvs(CbusNode thisnode) { 172 return sentOutstandingNvs(thisnode) || sentOutstandingEvs(thisnode); 173 } 174 175 private boolean sentOutstandingParam(CbusNode thisnode) { 176 if ( thisnode!=null && thisnode.getNodeParamManager().getOutstandingParams() > 0 ) { // this params 177 thisnode.getNodeParamManager().sendRequestNextParam(); 178 return true; 179 } 180 return false; 181 } 182 183 private boolean sentUrgentFetches() { 184 185 // If a node is selected in the node manager the details for this are fetched next 186 CbusNode _urgentNode = getNodeByNodeNum(urgentNode); 187 CbusNode _beforeNode = getNodeByNodeNum(nodebefore); 188 CbusNode _afterNode = getNodeByNodeNum(nodeafter); 189 190 if (sentOutstandingNvsOrEvs(_urgentNode)) { 191 return true; 192 } 193 194 if ( sentOutstandingNvs(_afterNode) || sentOutstandingNvs(_beforeNode) ){ 195 return true; 196 } 197 198 if (sentOutstandingEvs(_afterNode) || sentOutstandingEvs(_beforeNode) ) { 199 return true; 200 } 201 202 // the node selected in table has been synched, 203 // along with the row above and below in case user scolls 204 urgentActive=false; 205 206 return false; 207 208 } 209 210 /** 211 * Send the next parameter request, ev var request or nv request. 212 * Triggered from either background or active fetch. 213 * Triggers loading the node backup xml file 214 * Triggers the check for node data fetch complete 215 * 216 * The order of the fetch changes depending on 217 * If node is a Command station 218 * If a node is currently selected in a node table pane 219 * The node above or below the currently selected row 220 * If event or nv tab is displayed in a node table pane 221 * 222 * Default order is Params 0,1,3,6,5,7,2, event total, 223 * remaining parameters, NVs, event index, event vars. 224 */ 225 protected void sendNextBackgroundFetch(){ 226 227 if ( getAnyNodeInLearnMode()>0 || hasActiveTimers()){ 228 return; 229 } 230 231 if (sentCommandStationFetch() || sentAnAllNodeParam()){ 232 return; 233 } 234 235 if (sentUrgentFetches()){ 236 return; 237 } 238 239 // default lookup routine for NV's then Events. 240 // parameters should already be known 241 for (int i = 0; i < getRowCount(); i++) { 242 if (sentOutstandingNvsOrEvs(_mainArray.get(i))){ 243 return; 244 } 245 } 246 247 // if all done dispose trickle fetch 248 if ( trickleFetch != null ) { 249 trickleFetch.dispose(); 250 } 251 trickleFetch = null; 252 } 253 254 // private static final Logger log = LoggerFactory.getLogger(CbusBasicNodeTableFetch.class); 255}