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 && thisnode.getNodeEventManager().getOutstandingEvVars() > 0 ) { // this events
140            thisnode.getNodeEventManager().sendNextEvVarToFetch();
141            return true;
142        }
143        return false;
144    }
145    
146    private boolean sentOutstandingNvsOrEvs(CbusNode thisnode) {
147        return sentOutstandingNvs(thisnode) || sentOutstandingEvs(thisnode);
148    }
149    
150    private boolean sentOutstandingParam(CbusNode thisnode) {
151        if ( thisnode!=null && thisnode.getNodeParamManager().getOutstandingParams() > 0 ) { // this params
152            thisnode.getNodeParamManager().sendRequestNextParam();
153            return true;
154        }
155        return false;
156    }
157    
158    private boolean sentUrgentFetches() {
159    
160        // If a node is selected in the node manager the details for this are fetched next
161        CbusNode _urgentNode = getNodeByNodeNum(urgentNode);
162        CbusNode _beforeNode = getNodeByNodeNum(nodebefore);
163        CbusNode _afterNode = getNodeByNodeNum(nodeafter);
164        
165        if (sentOutstandingNvsOrEvs(_urgentNode)) {
166            return true;
167        }
168        
169        if ( sentOutstandingNvs(_afterNode) || sentOutstandingNvs(_beforeNode) ){
170            return true;
171        }
172
173        if (sentOutstandingEvs(_afterNode) || sentOutstandingEvs(_beforeNode) ) {
174            return true;
175        }
176            
177        // the node selected in table has been synched, 
178        // along with the row above and below in case user scolls
179        urgentActive=false;
180    
181        return false;
182    
183    }
184    
185    /**
186     * Send the next parameter request, ev var request or nv request.
187     * Triggered from either background or active fetch.
188     * Triggers loading the node backup xml file
189     * Triggers the check for node data fetch complete
190     *
191     * The order of the fetch changes depending on
192     * If node is a Command station
193     * If a node is currently selected in a node table pane
194     * The node above or below the currently selected row
195     * If event or nv tab is displayed in a node table pane
196     *
197     * Default order is Params 0,1,3,6,5,7,2, event total, 
198     * remaining parameters, NVs, event index, event vars.
199     */ 
200    protected void sendNextBackgroundFetch(){
201        
202        if ( getAnyNodeInLearnMode()>0 || hasActiveTimers()){
203            return;
204        }
205        
206        if (sentCommandStationFetch() || sentAnAllNodeParam()){
207            return;
208        }
209        
210        if (sentUrgentFetches()){
211            return;
212        }
213        
214        // default lookup routine for NV's then Events.
215        // parameters should already be known
216        for (int i = 0; i < getRowCount(); i++) {
217            if (sentOutstandingNvsOrEvs(_mainArray.get(i))){
218                return;
219            }
220        }
221        
222        // if all done dispose trickle fetch
223        if ( trickleFetch != null ) {
224            trickleFetch.dispose();
225        }
226        trickleFetch = null;
227    }
228    
229    // private final static Logger log = LoggerFactory.getLogger(CbusBasicNodeTableFetch.class);
230}