001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.util.sendable; 006 007import java.lang.ref.WeakReference; 008import java.util.Arrays; 009import java.util.Map; 010import java.util.WeakHashMap; 011 012/** 013 * The SendableRegistry class is the public interface for registering sensors and actuators for use 014 * on dashboards. 015 */ 016@SuppressWarnings("PMD.AvoidCatchingGenericException") 017public final class SendableRegistry { 018 private static class Component implements AutoCloseable { 019 Component() {} 020 021 Component(Sendable sendable) { 022 m_sendable = new WeakReference<>(sendable); 023 } 024 025 @Override 026 public void close() throws Exception { 027 m_builder.close(); 028 for (AutoCloseable data : m_data) { 029 if (data != null) { 030 data.close(); 031 } 032 } 033 } 034 035 WeakReference<Sendable> m_sendable; 036 SendableBuilder m_builder; 037 String m_name; 038 String m_subsystem = "Ungrouped"; 039 AutoCloseable[] m_data; 040 041 void setName(String moduleType, int channel) { 042 m_name = moduleType + "[" + channel + "]"; 043 } 044 045 void setName(String moduleType, int moduleNumber, int channel) { 046 m_name = moduleType + "[" + moduleNumber + "," + channel + "]"; 047 } 048 } 049 050 private static final Map<Object, Component> components = new WeakHashMap<>(); 051 private static int nextDataHandle; 052 053 private static Component getOrAdd(Sendable sendable) { 054 Component comp = components.get(sendable); 055 if (comp == null) { 056 comp = new Component(sendable); 057 components.put(sendable, comp); 058 } else { 059 if (comp.m_sendable == null) { 060 comp.m_sendable = new WeakReference<>(sendable); 061 } 062 } 063 return comp; 064 } 065 066 private SendableRegistry() { 067 throw new UnsupportedOperationException("This is a utility class!"); 068 } 069 070 /** 071 * Adds an object to the registry. 072 * 073 * @param sendable object to add 074 * @param name component name 075 */ 076 public static synchronized void add(Sendable sendable, String name) { 077 Component comp = getOrAdd(sendable); 078 comp.m_name = name; 079 } 080 081 /** 082 * Adds an object to the registry. 083 * 084 * @param sendable object to add 085 * @param moduleType A string that defines the module name in the label for the value 086 * @param channel The channel number the device is plugged into 087 */ 088 public static synchronized void add(Sendable sendable, String moduleType, int channel) { 089 Component comp = getOrAdd(sendable); 090 comp.setName(moduleType, channel); 091 } 092 093 /** 094 * Adds an object to the registry. 095 * 096 * @param sendable object to add 097 * @param moduleType A string that defines the module name in the label for the value 098 * @param moduleNumber The number of the particular module type 099 * @param channel The channel number the device is plugged into 100 */ 101 public static synchronized void add( 102 Sendable sendable, String moduleType, int moduleNumber, int channel) { 103 Component comp = getOrAdd(sendable); 104 comp.setName(moduleType, moduleNumber, channel); 105 } 106 107 /** 108 * Adds an object to the registry. 109 * 110 * @param sendable object to add 111 * @param subsystem subsystem name 112 * @param name component name 113 */ 114 public static synchronized void add(Sendable sendable, String subsystem, String name) { 115 Component comp = getOrAdd(sendable); 116 comp.m_name = name; 117 comp.m_subsystem = subsystem; 118 } 119 120 /** 121 * Adds a child object to an object. Adds the child object to the registry if it's not already 122 * present. 123 * 124 * @param parent parent object 125 * @param child child object 126 */ 127 public static synchronized void addChild(Sendable parent, Object child) { 128 Component comp = components.get(child); 129 if (comp == null) { 130 comp = new Component(); 131 components.put(child, comp); 132 } 133 // comp.m_parent = new WeakReference<>(parent); 134 } 135 136 /** 137 * Removes an object from the registry. 138 * 139 * @param sendable object to remove 140 * @return true if the object was removed; false if it was not present 141 */ 142 public static synchronized boolean remove(Sendable sendable) { 143 Component comp = components.remove(sendable); 144 if (comp != null) { 145 try { 146 comp.close(); 147 } catch (Exception e) { 148 // ignore 149 } 150 } 151 return comp != null; 152 } 153 154 /** 155 * Determines if an object is in the registry. 156 * 157 * @param sendable object to check 158 * @return True if in registry, false if not. 159 */ 160 public static synchronized boolean contains(Sendable sendable) { 161 return components.containsKey(sendable); 162 } 163 164 /** 165 * Gets the name of an object. 166 * 167 * @param sendable object 168 * @return Name (empty if object is not in registry) 169 */ 170 public static synchronized String getName(Sendable sendable) { 171 Component comp = components.get(sendable); 172 if (comp == null) { 173 return ""; 174 } 175 return comp.m_name; 176 } 177 178 /** 179 * Sets the name of an object. 180 * 181 * @param sendable object 182 * @param name name 183 */ 184 public static synchronized void setName(Sendable sendable, String name) { 185 Component comp = components.get(sendable); 186 if (comp != null) { 187 comp.m_name = name; 188 } 189 } 190 191 /** 192 * Sets the name of an object with a channel number. 193 * 194 * @param sendable object 195 * @param moduleType A string that defines the module name in the label for the value 196 * @param channel The channel number the device is plugged into 197 */ 198 public static synchronized void setName(Sendable sendable, String moduleType, int channel) { 199 Component comp = components.get(sendable); 200 if (comp != null) { 201 comp.setName(moduleType, channel); 202 } 203 } 204 205 /** 206 * Sets the name of an object with a module and channel number. 207 * 208 * @param sendable object 209 * @param moduleType A string that defines the module name in the label for the value 210 * @param moduleNumber The number of the particular module type 211 * @param channel The channel number the device is plugged into 212 */ 213 public static synchronized void setName( 214 Sendable sendable, String moduleType, int moduleNumber, int channel) { 215 Component comp = components.get(sendable); 216 if (comp != null) { 217 comp.setName(moduleType, moduleNumber, channel); 218 } 219 } 220 221 /** 222 * Sets both the subsystem name and device name of an object. 223 * 224 * @param sendable object 225 * @param subsystem subsystem name 226 * @param name device name 227 */ 228 public static synchronized void setName(Sendable sendable, String subsystem, String name) { 229 Component comp = components.get(sendable); 230 if (comp != null) { 231 comp.m_name = name; 232 comp.m_subsystem = subsystem; 233 } 234 } 235 236 /** 237 * Gets the subsystem name of an object. 238 * 239 * @param sendable object 240 * @return Subsystem name (empty if object is not in registry) 241 */ 242 public static synchronized String getSubsystem(Sendable sendable) { 243 Component comp = components.get(sendable); 244 if (comp == null) { 245 return ""; 246 } 247 return comp.m_subsystem; 248 } 249 250 /** 251 * Sets the subsystem name of an object. 252 * 253 * @param sendable object 254 * @param subsystem subsystem name 255 */ 256 public static synchronized void setSubsystem(Sendable sendable, String subsystem) { 257 Component comp = components.get(sendable); 258 if (comp != null) { 259 comp.m_subsystem = subsystem; 260 } 261 } 262 263 /** 264 * Gets a unique handle for setting/getting data with setData() and getData(). 265 * 266 * @return Handle 267 */ 268 public static synchronized int getDataHandle() { 269 return nextDataHandle++; 270 } 271 272 /** 273 * Associates arbitrary data with an object in the registry. 274 * 275 * @param sendable object 276 * @param handle data handle returned by getDataHandle() 277 * @param data data to set 278 * @return Previous data (may be null). If non-null, caller is responsible for calling close(). 279 */ 280 @SuppressWarnings("PMD.CompareObjectsWithEquals") 281 public static synchronized AutoCloseable setData( 282 Sendable sendable, int handle, AutoCloseable data) { 283 Component comp = components.get(sendable); 284 if (comp == null) { 285 return null; 286 } 287 AutoCloseable rv = null; 288 if (comp.m_data == null) { 289 comp.m_data = new AutoCloseable[handle + 1]; 290 } else if (handle < comp.m_data.length) { 291 rv = comp.m_data[handle]; 292 } else { 293 comp.m_data = Arrays.copyOf(comp.m_data, handle + 1); 294 } 295 if (comp.m_data[handle] != data) { 296 if (comp.m_data[handle] != null) { 297 try { 298 comp.m_data[handle].close(); 299 } catch (Exception e) { 300 // ignore 301 } 302 } 303 comp.m_data[handle] = data; 304 } 305 return rv; 306 } 307 308 /** 309 * Gets arbitrary data associated with an object in the registry. 310 * 311 * @param sendable object 312 * @param handle data handle returned by getDataHandle() 313 * @return data (may be null if none associated) 314 */ 315 public static synchronized Object getData(Sendable sendable, int handle) { 316 Component comp = components.get(sendable); 317 if (comp == null || comp.m_data == null || handle >= comp.m_data.length) { 318 return null; 319 } 320 return comp.m_data[handle]; 321 } 322 323 /** 324 * Publishes an object in the registry to a builder. 325 * 326 * @param sendable object 327 * @param builder sendable builder 328 */ 329 public static synchronized void publish(Sendable sendable, SendableBuilder builder) { 330 Component comp = getOrAdd(sendable); 331 if (comp.m_builder != null) { 332 try { 333 comp.m_builder.close(); 334 } catch (Exception e) { 335 // ignore 336 } 337 } 338 comp.m_builder = builder; // clear any current builder 339 sendable.initSendable(comp.m_builder); 340 comp.m_builder.update(); 341 } 342 343 /** 344 * Updates network table information from an object. 345 * 346 * @param sendable object 347 */ 348 public static synchronized void update(Sendable sendable) { 349 Component comp = components.get(sendable); 350 if (comp != null && comp.m_builder != null) { 351 comp.m_builder.update(); 352 } 353 } 354}