This repository has been archived by the owner on Jan 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
850a86a
commit 2069112
Showing
15 changed files
with
625 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,125 @@ | ||
package com.team766.framework3; | ||
|
||
import com.team766.hal.RobotProvider; | ||
import com.team766.logging.Category; | ||
import com.team766.logging.Logger; | ||
import com.team766.logging.Severity; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.NoSuchElementException; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* Bus for broadcasting and querying {@link Status} of different parts of the robot (eg state of a Mechanism). | ||
* This is a Singleton, accessed via {@link #getInstance()}. Producers can call {@link #publish} to publish | ||
* their latest {@link Status}. Consumers can call {@link #getStatus(Class)} with the class Object | ||
* (eg, {@code MyStatus.class}) for the {@link Status} they are interested in querying, to get the latest | ||
* published {@link Status}. | ||
* This is a Singleton. Producers can call {@link #publish} to publish their latest {@link Status}. | ||
* Consumers can call {@link #getStatus(Class)} with the class Object (eg, {@code MyStatus.class}) | ||
* for the {@link Status} they are interested in querying, to get the latest published {@link Status}. | ||
*/ | ||
public class StatusBus implements LoggingBase { | ||
|
||
private static StatusBus s_instance = new StatusBus(); | ||
private final Map<Class<? extends Status>, Status> statuses = new LinkedHashMap<>(); | ||
public class StatusBus { | ||
|
||
/** | ||
* Get the Singleton instance of the {@link StatusBus}. | ||
* @param status The {@link Status} that was published | ||
* @param timestamp The time at which the Status was published | ||
*/ | ||
public static StatusBus getInstance() { | ||
return s_instance; | ||
public record Entry<T extends Status>(T status, double timestamp) { | ||
public double age() { | ||
return RobotProvider.instance.getClock().getTime() - timestamp; | ||
} | ||
} | ||
|
||
// TODO: would this be helpful? | ||
// private void clear() { | ||
// statuses.clear(); | ||
// } | ||
private static final Map<Class<?>, Entry<?>> statuses = new LinkedHashMap<>(); | ||
|
||
/** | ||
* Publish a new {@link Status} for the given specific class of {@link Status}. Each producer will | ||
* Remove all published @{link Status}es from the StatusBus. | ||
*/ | ||
public static void clear() { | ||
statuses.clear(); | ||
} | ||
|
||
/** | ||
* Publish a new {@link Status} for the given specific class of {@link Status}. Each producer will | ||
* create their own implementation of the {@link Status} interface to contain its state information. | ||
* | ||
* This method also logs the Status to diagnostic logs. | ||
*/ | ||
public void publish(Status status) { | ||
statuses.put(status.getClass(), status); | ||
// TODO: also publish to data logs | ||
log("StatusBus received Status (" + status.getClass().getName() + "): " + status); | ||
public static <S extends Record & Status> Entry<S> publishStatus(S status) { | ||
var entry = new Entry<>(status, RobotProvider.instance.getClock().getTime()); | ||
statuses.put(status.getClass(), entry); | ||
// TODO(MF3): also publish to data logs | ||
Logger.get(Category.FRAMEWORK) | ||
.logRaw( | ||
Severity.INFO, | ||
"StatusBus received Status (" | ||
+ status.getClass().getName() | ||
+ "): " | ||
+ status); | ||
return entry; | ||
} | ||
|
||
/** | ||
* Gets the latest published {@link Status} for the given class of {@link Status}. Each producer will | ||
* create their own implementation of the {@link Status} interface to contain its state information. Each | ||
* Gets the latest published {@link Status} for the given class of {@link Status}, along with | ||
* additional metadata about how the Status was published. Each producer will create their own | ||
* implementation of the {@link Status} interface to contain its state information. Each | ||
* consumer will need to know the {@link Status} class a priori, in order to query it. | ||
* | ||
* @param <S> The specific {@link Status} class of interest. | ||
* @param statusClass The Class object for the Status, eg {@code MyStatus.class}. | ||
* @return The latest published {@link Status} or null if the {@link Status} hasn't been published. | ||
* @return | ||
* A {@link StatusBus.Entry} containing the latest published {@link Status}, or an empty | ||
* Optional if the {@link Status} hasn't been published. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public <S extends Status> S getStatus(Class<S> statusClass) { | ||
return (S) statuses.get(statusClass); | ||
public static <S extends Status> Optional<Entry<S>> getStatusEntry(Class<S> statusClass) { | ||
return Optional.ofNullable((Entry<S>) statuses.get(statusClass)); | ||
} | ||
|
||
@Override | ||
public Category getLoggerCategory() { | ||
return Category.FRAMEWORK; | ||
/** | ||
* Gets the latest published {@link Status} for the given class of {@link Status}. Each producer | ||
* will create their own implementation of the {@link Status} interface to contain its state | ||
* information. Each consumer will need to know the {@link Status} class a priori, in order to | ||
* query it. | ||
* | ||
* @param <S> The specific {@link Status} class of interest. | ||
* @param statusClass The Class object for the Status, eg {@code MyStatus.class}. | ||
* @return | ||
* The latest published {@link Status} or an empty Optional if the {@link Status} hasn't | ||
* been published. | ||
*/ | ||
public static <S extends Status> Optional<S> getStatus(Class<S> statusClass) { | ||
return getStatusEntry(statusClass).map(Entry<S>::status); | ||
} | ||
|
||
/** | ||
* Gets the latest published {@link Status} for the given class of {@link Status}. Each producer | ||
* will create their own implementation of the {@link Status} interface to contain its state | ||
* information. Each consumer will need to know the {@link Status} class a priori, in order to | ||
* query it. | ||
* | ||
* @param <S> The specific {@link Status} class of interest. | ||
* @param statusClass The Class object for the Status, eg {@code MyStatus.class}. | ||
* @return The latest published {@link Status} | ||
* @throws NoSuchElementException if the {@link Status} hasn't been published | ||
*/ | ||
public static <S extends Status> S getStatusOrThrow(Class<S> statusClass) { | ||
return getStatus(statusClass).orElseThrow(); | ||
} | ||
|
||
/** | ||
* Gets the latest published {@link Status} for the given class of {@link Status}, then applies | ||
* the given function to the Status and returns the result. Each producer will create their own | ||
* implementation of the {@link Status} interface to contain its state information. Each | ||
* consumer will need to know the {@link Status} class a priori, in order to query it. | ||
* | ||
* @param <S> The specific {@link Status} class of interest. | ||
* @param <V> The return type of the Function | ||
* @param statusClass The Class object for the Status, eg {@code MyStatus.class}. | ||
* @return | ||
* The result of the Function applied to the latest published {@link Status} or an empty | ||
* Optional if the {@link Status} hasn't been published. | ||
*/ | ||
public static <S extends Status, V> Optional<V> getStatusValue( | ||
Class<S> statusClass, Function<S, V> getter) { | ||
return getStatusEntry(statusClass).map(s -> getter.apply(s.status())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.