001package jmri.jmrix.can.cbus; 002 003import java.util.EnumSet; 004 005import javax.annotation.CheckForNull; 006import javax.annotation.Nonnull; 007import javax.swing.JLabel; 008import javax.swing.tree.DefaultMutableTreeNode; 009 010import jmri.jmrix.AbstractMessage; 011import jmri.jmrix.can.cbus.swing.CbusFilterTreePane; 012 013 014/** 015 * Class to implement filtering of CBUS frames. 016 * Long event OPCs are not altered for a node number of 0 017 * @author Steve Young (C) 2018, 2020 018 */ 019public class CbusFilter { 020 021 private final java.util.List<FilterHolder> list = new java.util.concurrent.CopyOnWriteArrayList<>(); 022 023 private final CbusFilterTreePane filterTreePane; 024 private int _evMin = 0; 025 private int _evMax = 65535; 026 private int _ndMin = 0; 027 private int _ndMax = 65535; 028 029 private long filteredMessage = 0; 030 private long filteredReply = 0; 031 private long passedMessage = 0; 032 private long passedReply = 0; 033 034 public static final int CFMAXCATS =CbusFilterType.values().length; 035 036 public static final String ROOT_NODE_TEXT = Bundle.getMessage("FilterAllFrames"); 037 038 /** 039 * Creates a new instance of CbusFilter 040 * @param filterPane The Instance Pane 041 */ 042 public CbusFilter(CbusFilterTreePane filterPane) { 043 filterTreePane = filterPane; 044 getTree(); 045 } 046 047 /** 048 * Filter CanMessage or CanReply. 049 * 050 * @param test Message to Test 051 * @return Filter number which failed, else -1 052 */ 053 public int filter( @Nonnull AbstractMessage test) { 054 055 int nodeNum = CbusMessage.getNodeNumber(test); 056 if ( nodeNum > 0 ) { 057 int nodeFilter = checknode(nodeNum); 058 if ( nodeFilter > -1 ) { 059 incrementTotals(test, nodeFilter); 060 return nodeFilter; 061 } 062 } 063 064 for (CbusFilterType singleFilter : CbusFilterType.allFilters(test.getElement(0))) { 065 int toReturn = singleFilter.action(test,this); 066 if ( toReturn>-1){ 067 incrementTotals(test, toReturn); 068 return toReturn; 069 } 070 else if ( toReturn==-2){ // Extended or RTR CAN Frame, No OPC's to filter. 071 break; 072 } 073 } 074 incrementTotals(test, -1); 075 return -1; 076 } 077 078 private void incrementTotals( @Nonnull AbstractMessage test, int result){ 079 incrementCount(result); 080 if ( test instanceof jmri.jmrix.can.CanReply ) { 081 if ( result > -1 ) { 082 this.filteredReply++; 083 } else { 084 this.passedReply++; 085 } 086 } else { 087 if ( result > -1 ) { 088 this.filteredMessage++; 089 } else { 090 this.passedMessage++; 091 } 092 } 093 } 094 095 public void setFiltersByName( java.util.Set<String> activeFilters ) { 096 list.forEach( f -> f.setActive(activeFilters.contains(f.node.toString()))); 097 } 098 099 public boolean isFilterActive(int filterNum) { 100 return list.get(filterNum).active; 101 } 102 103 /** 104 * Perform Node checks for a given node number. 105 * If a new Node Number is found, is added to the main Node List 106 * and a new filter created. 107 * 108 * @param node Node Number 109 * @return Filter number which failed, else -1 110 */ 111 private int checknode(int node ) { 112 113 String nodeNum = String.valueOf(node); 114 115 FilterHolder fh = getFilterHolder(nodeNum); 116 if ( fh == null ) { 117 118 DefaultMutableTreeNode snode = new DefaultMutableTreeNode(nodeNum); 119 fh = new FilterHolder(snode, node); 120 list.add(fh); 121 122 DefaultMutableTreeNode ndnd = this.getTreeNode(CbusFilterType.CFNODE); 123 ndnd.add(snode); 124 125 if (filterTreePane !=null) { 126 filterTreePane.reset(); 127 } 128 } 129 130 if ( list.get(CbusFilterType.CFNODE.ordinal()).active){ 131 return CbusFilterType.CFNODE.ordinal(); 132 } 133 134 if ( fh.active ){ 135 return list.indexOf(fh); 136 } 137 return -1; 138 } 139 140 @CheckForNull 141 private FilterHolder getFilterHolder( @Nonnull String treeNodeName) { 142 for ( FilterHolder fh : list ) { 143 if ( treeNodeName.equals(fh.node.toString()) ) { 144 return fh; 145 } 146 } 147 return null; 148 } 149 150 /** 151 * Increment Filter count for a given filter ID. 152 * @param filternum Filter ID 153 */ 154 private void incrementCount(int filternum ){ 155 if ( filternum >= 0 ) { 156 FilterHolder node = list.get(filternum); 157 if ( node != null ) { 158 node.filterCount++; 159 } 160 } 161 } 162 163 /** 164 * Set a single Filter to pass or filter. 165 * @param id Filter ID 166 * @param trueorfalse true to filter, false to pass through. 167 */ 168 public void setFilter(int id, boolean trueorfalse) { 169 list.get(id).setActive(trueorfalse); 170 } 171 172 /** 173 * Set the event or node min and max values. 174 * 175 * @param filter CFEVENTMIN, CFEVENTMAX, CFNODEMIN or CFNODEMAX 176 * @param val min or max value 177 */ 178 public void setMinMax(@Nonnull CbusFilterType filter, int val){ 179 switch (filter) { 180 case CFEVENTMIN: 181 _evMin = val; 182 break; 183 case CFEVENTMAX: 184 _evMax = val; 185 break; 186 case CFNODEMIN: 187 _ndMin = val; 188 break; 189 case CFNODEMAX: 190 _ndMax = val; 191 break; 192 default: 193 break; 194 } 195 } 196 197 public long getFilteredMessage(){ 198 return filteredMessage; 199 } 200 201 public long getFilteredReply(){ 202 return filteredReply; 203 } 204 205 public long getPassedMessage(){ 206 return passedMessage; 207 } 208 209 public long getPassedReply(){ 210 return passedReply; 211 } 212 213 /** 214 * Get Minimum Event Number. 215 * @return Minimum Event 216 */ 217 public int getEvMin() { 218 return _evMin; 219 } 220 221 /** 222 * Get Maximum Event Number. 223 * @return Maximum Event 224 */ 225 public int getEvMax() { 226 return _evMax; 227 } 228 229 /** 230 * Get Minimum Node Number. 231 * @return Minimum Node 232 */ 233 public int getNdMin() { 234 return _ndMin; 235 } 236 237 /** 238 * Get Maximum Node Number. 239 * @return Maximum Node 240 */ 241 public int getNdMax() { 242 return _ndMax; 243 } 244 245 private DefaultMutableTreeNode root; 246 247 public final synchronized DefaultMutableTreeNode getTree(){ 248 if ( root == null ) { 249 root = new DefaultMutableTreeNode(ROOT_NODE_TEXT); 250 EnumSet.allOf(CbusFilterType.class).forEach( singleFilter -> { 251 DefaultMutableTreeNode snode = new DefaultMutableTreeNode(singleFilter.getName()); 252 var category = singleFilter.getCategory(); 253 if ( category == null ) { 254 root.add(snode); 255 } else { 256 getTreeNode(category).add(snode); 257 } 258 list.add( new FilterHolder(snode) ); 259 260 }); 261 262 } 263 return root; 264 } 265 266 @Nonnull 267 private DefaultMutableTreeNode getTreeNode( @Nonnull CbusFilterType type ) { 268 for ( FilterHolder f : list) { 269 if ( type.getName().equals(f.node.toString() ) ) { 270 return f.node; 271 } 272 } 273 throw new IllegalArgumentException("type Not found in list of nodes"); 274 } 275 276 public void resetCounts() { 277 list.forEach( FilterHolder::resetCount); 278 } 279 280 @CheckForNull 281 public JLabel getNumberFilteredLabel( DefaultMutableTreeNode node ) { 282 FilterHolder f = getFilterHolder(node.toString()); 283 if ( f != null ) { 284 return f.getCountLabel(); 285 } 286 return null; 287 } 288 289 public int getNodeNumber( DefaultMutableTreeNode node ) { 290 FilterHolder f = getFilterHolder(node.toString()); 291 if ( f != null ) { 292 return f.nodeNum; 293 } 294 return -1; 295 } 296 297 private static class FilterHolder { 298 299 final DefaultMutableTreeNode node; 300 boolean active; 301 int filterCount; 302 final int nodeNum; 303 private static final java.awt.Color COUNT_COLOUR = new java.awt.Color(139,0,0); 304 305 private FilterHolder(DefaultMutableTreeNode node) { 306 this.node = node; 307 nodeNum = -1; 308 } 309 310 private FilterHolder(DefaultMutableTreeNode node, int nodeNumber) { 311 this.node = node; 312 this.nodeNum = nodeNumber; 313 } 314 315 void resetCount() { 316 filterCount = 0; 317 } 318 319 JLabel getCountLabel() { 320 JLabel toRet = new JLabel( " " + filterCount + " "); 321 toRet.setForeground( COUNT_COLOUR ); 322 return toRet; 323 } 324 325 void setActive( boolean newVal) { 326 active = newVal; 327 } 328 329 } 330 331 // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusFilter.class); 332 333}