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 org.wpilib.hardware.expansionhub; 006 007import org.wpilib.hardware.hal.HAL; 008import org.wpilib.networktables.BooleanSubscriber; 009import org.wpilib.networktables.NetworkTableInstance; 010import org.wpilib.system.SystemServer; 011 012/** This class controls a REV ExpansionHub plugged in over USB to Systemcore. */ 013public class ExpansionHub implements AutoCloseable { 014 private static class DataStore implements AutoCloseable { 015 public final int m_usbId; 016 private int m_refCount; 017 private int m_reservedMotorMask; 018 private int m_reservedServoMask; 019 private final Object m_reserveLock = new Object(); 020 021 private final BooleanSubscriber m_hubConnectedSubscriber; 022 023 DataStore(int usbId) { 024 m_usbId = usbId; 025 m_storeMap[usbId] = this; 026 027 NetworkTableInstance systemServer = SystemServer.getSystemServer(); 028 029 m_hubConnectedSubscriber = 030 systemServer.getBooleanTopic("/rhsp/" + usbId + "/connected").subscribe(false); 031 } 032 033 @Override 034 public void close() { 035 m_storeMap[m_usbId] = null; 036 } 037 038 public void addRef() { 039 m_refCount++; 040 } 041 042 public void removeRef() { 043 m_refCount--; 044 if (m_refCount == 0) { 045 this.close(); 046 } 047 } 048 } 049 050 private static final DataStore[] m_storeMap = new DataStore[4]; 051 052 private static void checkUsbId(int usbId) { 053 if (usbId < 0 || usbId > 3) { 054 throw new IllegalArgumentException("USB Port " + usbId + " out of range"); 055 } 056 } 057 058 private static DataStore getForUsbId(int usbId) { 059 checkUsbId(usbId); 060 synchronized (m_storeMap) { 061 DataStore store = m_storeMap[usbId]; 062 if (store == null) { 063 store = new DataStore(usbId); 064 } 065 store.addRef(); 066 return store; 067 } 068 } 069 070 private static void freeHub(DataStore store) { 071 synchronized (m_storeMap) { 072 store.removeRef(); 073 } 074 } 075 076 private final DataStore m_dataStore; 077 078 /** 079 * Constructs a new ExpansionHub for a given USB ID 080 * 081 * <p>Multiple instances can be constructed, but will point to the same backing object with a ref 082 * count. 083 * 084 * @param usbId The USB Port ID the hub is plugged into. 085 */ 086 public ExpansionHub(int usbId) { 087 m_dataStore = getForUsbId(usbId); 088 } 089 090 /** 091 * Closes an ExpansionHub object. Will not close any other instances until the last instance is 092 * closed. 093 */ 094 @Override 095 public void close() { 096 freeHub(m_dataStore); 097 } 098 099 boolean checkServoChannel(int channel) { 100 return channel >= 0 && channel < 6; 101 } 102 103 boolean checkAndReserveServo(int channel) { 104 int mask = 1 << channel; 105 synchronized (m_dataStore.m_reserveLock) { 106 if ((m_dataStore.m_reservedServoMask & mask) != 0) { 107 return false; 108 } 109 m_dataStore.m_reservedServoMask |= mask; 110 return true; 111 } 112 } 113 114 void unreserveServo(int channel) { 115 int mask = 1 << channel; 116 synchronized (m_dataStore.m_reserveLock) { 117 m_dataStore.m_reservedServoMask &= ~mask; 118 } 119 } 120 121 boolean checkMotorChannel(int channel) { 122 return channel >= 0 && channel < 4; 123 } 124 125 boolean checkAndReserveMotor(int channel) { 126 int mask = 1 << channel; 127 synchronized (m_dataStore.m_reserveLock) { 128 if ((m_dataStore.m_reservedMotorMask & mask) != 0) { 129 return false; 130 } 131 m_dataStore.m_reservedMotorMask |= mask; 132 return true; 133 } 134 } 135 136 void unreserveMotor(int channel) { 137 int mask = 1 << channel; 138 synchronized (m_dataStore.m_reserveLock) { 139 m_dataStore.m_reservedMotorMask &= ~mask; 140 } 141 } 142 143 void reportUsage(String device, String data) { 144 HAL.reportUsage("ExpansionHub[" + m_dataStore.m_usbId + "]/" + device, data); 145 } 146 147 /** 148 * Constructs a servo at the requested channel on this hub. 149 * 150 * <p>Only a single instance of each servo per hub can be constructed at a time. 151 * 152 * @param channel The servo channel 153 * @return Servo object 154 */ 155 public ExpansionHubServo makeServo(int channel) { 156 return new ExpansionHubServo(m_dataStore.m_usbId, channel); 157 } 158 159 /** 160 * Constructs a motor at the requested channel on this hub. 161 * 162 * <p>Only a single instance of each motor per hub can be constructed at a time. 163 * 164 * @param channel The motor channel 165 * @return Motor object 166 */ 167 public ExpansionHubMotor makeMotor(int channel) { 168 return new ExpansionHubMotor(m_dataStore.m_usbId, channel); 169 } 170 171 /** 172 * Gets if the hub is currently connected over USB. 173 * 174 * @return True if hub connection, otherwise false 175 */ 176 public boolean isHubConnected() { 177 return m_dataStore.m_hubConnectedSubscriber.get(false); 178 } 179 180 /** 181 * Gets the USB ID of this hub. 182 * 183 * @return The USB ID 184 */ 185 public int getUsbId() { 186 return m_dataStore.m_usbId; 187 } 188}