001package jmri.jmrix.can.cbus.node; 002 003import java.beans.PropertyChangeListener; 004import java.util.concurrent.CopyOnWriteArraySet; 005// import javax.annotation.Nonnull; 006 007import javax.annotation.CheckForNull; 008 009import jmri.jmrix.can.CanSystemConnectionMemo; 010import jmri.jmrix.can.TrafficController; 011import jmri.jmrix.can.cbus.CbusSend; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * Class to represent a node. 018 * 019 * @author Steve Young Copyright (C) 2019 020 */ 021public class CbusBasicNode { 022 protected CanSystemConnectionMemo _memo; 023 private int _nodeNumber; 024 025 private int _canId; 026 private boolean _inSetupMode; 027 private boolean _inlearnMode; 028 private boolean _inFLiMMode; 029 030 public CbusSend send; 031 032 // data members to hold contact with the property listeners 033 protected final CopyOnWriteArraySet<PropertyChangeListener> _listeners; 034 035 /** 036 * Create a new CbusBasicNodeWithChangeListener. 037 * 038 * @param connmemo The CAN Connection to use 039 * @param nodenumber The Node Number 040 */ 041 public CbusBasicNode ( @CheckForNull CanSystemConnectionMemo connmemo, int nodenumber ){ 042 _memo = connmemo; 043 _nodeNumber = nodenumber; 044 _canId = 1; 045 _inSetupMode = false; 046 _inlearnMode = false; 047 _inFLiMMode = true; 048 049 send = new CbusSend(_memo); 050 _listeners = new CopyOnWriteArraySet<>(); 051 } 052 053 /** 054 * Register for notification if any of the properties change. 055 * 056 * @param l The Listener to attach to Node 057 */ 058 public void addPropertyChangeListener(PropertyChangeListener l) { 059 // add only if not already registered 060 if (!_listeners.contains(l)) { 061 _listeners.add(l); 062 // log.info("Added listener {}, new size is {}", l,_listeners.size()); 063 } 064 } 065 066 /** 067 * Remove notification listener. 068 * @param l Listener to remove 069 */ 070 public void removePropertyChangeListener(PropertyChangeListener l) { 071 if (_listeners.contains(l)) { 072 _listeners.remove(l); 073 // log.info("Removed listener {}, new size is {}", l,_listeners.size()); 074 } 075 } 076 077 /** 078 * Trigger the notification of Node PropertyChangeListeners. 079 * 080 * Properties include 081 * PARAMETER, 082 * BACKUPS, 083 * SINGLENVUPDATE ( newValue NV index (0 is NV1, 5 is NV6) ) 084 * ALLNVUPDATE 085 * SINGLEEVUPDATE ( newValue event row ) 086 * ALLEVUPDATE 087 * DELETEEVCOMPLETE ( newValue Error String else empty String ) 088 * ADDEVCOMPLETE ( newValue Error String else null ) 089 * ADDALLEVCOMPLETE ( Event Teach Loop Completed, newValue error count ) 090 * TEACHNVCOMPLETE ( newValue error count ) 091 * NAMECHANGE 092 * 093 * @param property Node property 094 * @param oldValue Old Value 095 * @param newValue New Value 096 */ 097 protected void notifyPropertyChangeListener(String property, Object oldValue, Object newValue) { 098 if ((oldValue != null && oldValue.equals(newValue)) || (newValue != null && newValue.equals(oldValue))) { 099 log.error("notifyPropertyChangeListener without change"); 100 return; 101 } 102 _listeners.forEach((listener) -> { 103 listener.propertyChange(new java.beans.PropertyChangeEvent(this, property, oldValue, newValue)); 104 }); 105 } 106 107 /** 108 * Returns Node Number. 109 * 110 * @return Node Number,1-65535 111 */ 112 public int getNodeNumber() { 113 return _nodeNumber; 114 } 115 116 /** 117 * Set Node Number. 118 * @param newnumber Node Number, should be 1-65535 119 */ 120 public void setNodeNumber ( int newnumber ) { 121 _nodeNumber = newnumber; 122 notifyPropertyChangeListener("PARAMETER", null, null); 123 } 124 125 /** 126 * Set Node CAN ID. 127 * <p> 128 * Notifies PropertyChangeListener "CANID" only if the CAN ID has changed, 129 * as this is called for every CAN Frame received from the node. 130 * @param newcanid CAN ID of the node 131 */ 132 public final void setCanId ( int newcanid ) { 133 if ( _canId == newcanid ) { 134 return; 135 } 136 int oldCanId = _canId; 137 _canId = newcanid; 138 notifyPropertyChangeListener("CANID", oldCanId, _canId); 139 } 140 141 /** 142 * Set CAN ID by System Connection. 143 * <p> 144 * Leaves unchanged if no System Connection or Traffic Controller. 145 * @param memo System Connection of the Node. 146 */ 147 public final void setCanId( CanSystemConnectionMemo memo ) { 148 if ( memo != null ) { 149 TrafficController tc = memo.getTrafficController(); 150 if ( tc != null ) { 151 setCanId(tc.getCanid()); 152 } 153 } 154 } 155 156 /** 157 * Get the Node CAN ID. 158 * min 1 , ( max 128? ) 159 * @return CAN ID of the node, default 1. 160 */ 161 public int getNodeCanId() { 162 return _canId; 163 } 164 165 /** 166 * Set flag for this Node in Setup Mode. 167 * <p> 168 * Does NOT send instruction to actual node 169 * 170 * @param setup use true if in Setup, else false 171 */ 172 public void setNodeInSetupMode( boolean setup ) { 173 _inSetupMode = setup; 174 notifyPropertyChangeListener("PARAMETER", null, null); 175 } 176 177 /** 178 * Get if this Node is in Setup Mode. 179 * 180 * @return true if in Setup, else false 181 */ 182 public boolean getNodeInSetupMode() { 183 return _inSetupMode; 184 } 185 186 /** 187 * Set if the Node is in Learn Mode. 188 * Used to track node status, does NOT update Physical Node 189 * 190 * @param inlearnmode set true if in Learn else false 191 */ 192 public void setNodeInLearnMode( boolean inlearnmode) { 193 boolean oldLearnmode = _inlearnMode; 194 _inlearnMode = inlearnmode; 195 if (oldLearnmode != _inlearnMode) { 196 notifyPropertyChangeListener("LEARNMODE",oldLearnmode,_inlearnMode); 197 } 198 } 199 200 /** 201 * Get if the Node is in Learn Mode. 202 * <p> 203 * Defaults to false if unset 204 * 205 * @return true if in Learn else false 206 */ 207 public boolean getNodeInLearnMode() { 208 return _inlearnMode; 209 } 210 211 /** 212 * Set if the Node is in FLiM Mode. 213 * <p> 214 * Defaults to true if unset 215 * 216 * @param inFlimMode set true if in FlIM else false 217 */ 218 public void setNodeInFLiMMode( boolean inFlimMode ) { 219 _inFLiMMode = inFlimMode; 220 } 221 222 /** 223 * Get if the Node is in FLiM Mode. 224 * <p> 225 * Defaults to true if unset 226 * 227 * @return true if in FlIM else false 228 */ 229 public boolean getNodeInFLiMMode() { 230 return _inFLiMMode; 231 } 232 233 public CanSystemConnectionMemo getMemo() { 234 return _memo; 235 } 236 237 private static final Logger log = LoggerFactory.getLogger(CbusBasicNode.class); 238 239}