Skip to content

Commit

Permalink
Merge pull request #157 from FRC2706/rename-logging
Browse files Browse the repository at this point in the history
Rename logging
  • Loading branch information
ryanlarkin authored Apr 9, 2019
2 parents 16a9366 + f695359 commit d015e07
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 53 deletions.
26 changes: 26 additions & 0 deletions src/main/java/ca/team2706/frc/robot/ConnectionState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ca.team2706.frc.robot;

/**
* The status of the connection to the FMS and driverstation
*/
public enum ConnectionState {
/**
* FMS has been connected
*/
FMS_CONNECT,

/**
* FMS has been disconnected
*/
FMS_DISCONNECT,

/**
* Driverstation has been connnected
*/
DRIVERSTATION_CONNECT,

/**
* Driverstation has been disconnected
*/
DRIVERSTATION_DISCONNECT
}
3 changes: 0 additions & 3 deletions src/main/java/ca/team2706/frc/robot/OI.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@

import ca.team2706.frc.robot.commands.drivebase.AbsoluteRotateWithGyro;
import ca.team2706.frc.robot.commands.drivebase.CurvatureDriveWithJoystick;
import ca.team2706.frc.robot.commands.drivebase.DriverAssistVision;
import ca.team2706.frc.robot.commands.drivebase.DriverAssistVision.DriverAssistVisionTarget;
import ca.team2706.frc.robot.commands.intake.AfterEjectConditional;
import ca.team2706.frc.robot.commands.intake.EjectConditional;
import ca.team2706.frc.robot.commands.intake.arms.LowerArmsSafely;
import ca.team2706.frc.robot.commands.intake.arms.MovePlunger;
import ca.team2706.frc.robot.commands.intake.arms.RaiseArmsSafely;
import ca.team2706.frc.robot.commands.intake.cargo.AutoIntakeCargo;
import ca.team2706.frc.robot.commands.intake.cargo.RunIntakeOnJoystick;
import ca.team2706.frc.robot.commands.lift.*;
import ca.team2706.frc.robot.commands.ringlight.ToggleRingLight;
Expand Down
78 changes: 65 additions & 13 deletions src/main/java/ca/team2706/frc/robot/Robot.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public class Robot extends TimedRobot {
private static Robot latestInstance;

private final List<Consumer<RobotState>> stateListeners = new ArrayList<>();
private final List<Consumer<ConnectionState>> connectionListeners = new ArrayList<>();

private boolean driverStationConnected, fmsConnected;

public Robot() {
latestInstance = this;
Expand All @@ -47,6 +50,8 @@ public Robot() {
@Override
public void robotInit() {
setOnStateChange((state) -> Log.i("Robot State: " + state.name()));
setOnConnectionChange((state) -> Log.i("Connection State: " + state.name()));
setOnConnectionChange(Log::setupFMS);

onStateChange(RobotState.ROBOT_INIT);
isInitialized = true;
Expand Down Expand Up @@ -122,6 +127,15 @@ private void logInitialization(SubsystemStatus subsystemStatus, Subsystem subsys
*/
@Override
public void robotPeriodic() {
if (fmsConnected != DriverStation.getInstance().isFMSAttached()) {
fmsConnected = DriverStation.getInstance().isFMSAttached();
onConnectionChange(fmsConnected ? ConnectionState.FMS_CONNECT : ConnectionState.FMS_DISCONNECT);
}

if (driverStationConnected != DriverStation.getInstance().isDSAttached()) {
driverStationConnected = DriverStation.getInstance().isDSAttached();
onConnectionChange(driverStationConnected ? ConnectionState.DRIVERSTATION_CONNECT : ConnectionState.DRIVERSTATION_DISCONNECT);
}
}

/**
Expand Down Expand Up @@ -172,8 +186,6 @@ public void autonomousInit() {
// Iterate through each of the state-change listeners and call them.
onStateChange(RobotState.AUTONOMOUS);

logFMSData();

if (canRunAuto) {
selectorInit();
}
Expand Down Expand Up @@ -225,8 +237,6 @@ public void autonomousPeriodic() {
public void teleopInit() {
// Iterate through each of the state-change listeners and call them.
onStateChange(RobotState.TELEOP);

logFMSData();
}

/**
Expand Down Expand Up @@ -270,6 +280,57 @@ public static void main(String[] args) {
RobotBase.startRobot(Robot::new);
}

/**
* Adds a method to be called when the connection robot's state is changed.
*
* @param listener The connection listener to be added.
*/
public void addConnectionListener(Consumer<ConnectionState> listener) {
connectionListeners.add(listener);
}

/**
* Sets the given connection listener to be called when connection state is changed
*
* @param listener The listener to be invoked when the connection state is changed
*/
public static void setOnConnectionChange(Consumer<ConnectionState> listener) {
if (latestInstance != null) {
latestInstance.addConnectionListener(listener);
}
}

/**
* Removes a connection state listener.
*
* @param listener The listener to be removed.
*/
public void removeConnectionStateListener(Consumer<ConnectionState> listener) {
connectionListeners.remove(listener);
}

/**
* Removes a connection state listener so that it is no longer subscribed to robot state change events.
*
* @param listener The listener to be removed.
*/
public static void removeConnectionListener(Consumer<ConnectionState> listener) {
if (latestInstance != null) {
latestInstance.removeConnectionStateListener(listener);
}
}

/**
* Calls the connection state change event, executing the listeners.
*
* @param newState The robot's current (new) connection state.
*/
private void onConnectionChange(ConnectionState newState) {
// Make shallow copy of this.
ArrayList<Consumer<ConnectionState>> listeners = new ArrayList<>(connectionListeners);
listeners.forEach(action -> action.accept(newState));
}

/**
* Adds a method to be called when the robot's state is changed.
*
Expand Down Expand Up @@ -357,13 +418,4 @@ private static void shutdown() {
// Iterate through each of the state-change listeners and call them.
latestInstance.onStateChange(RobotState.SHUTDOWN);
}

/**
* Logs the current data in the FMS.
*/
private void logFMSData() {
if (DriverStation.getInstance().isFMSAttached()) {
Log.d("FMS: " + DriverStation.getInstance().getMatchType().name() + " " + DriverStation.getInstance().getMatchNumber() + " at " + DriverStation.getInstance().getMatchTime());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ public void execute() {
boolean override = Math.abs(forward) < Config.CURVATURE_OVERRIDE;

if (buttonPress.get()) {
if(!DriveBase.getInstance().isBrakeMode()) {
if (!DriveBase.getInstance().isBrakeMode()) {
DriveBase.getInstance().setBrakeMode(true);
}

DriveBase.getInstance().curvatureDrive(forward * 0.6, (override ? rotation / 2.5 : rotation), override);
} else {
if(DriveBase.getInstance().isBrakeMode()) {
if (DriveBase.getInstance().isBrakeMode()) {
DriveBase.getInstance().setBrakeMode(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,43 +399,43 @@ public void generateTrajectoryRobotToTarget(double distanceCameraToTarget_Camera
* case is F3).
*
* Points P1 or P2 can also be the origin of a coordinate frame. In this case, P1 is replaced by
* the name of the frame. Thus, if F1, F2, and F3 are coordinatre frames, then vF1ToF2_F3 represents
* the vector from the origin of frame F1 to the origin of frame F2 represented in the frame F3.
* the name of the frame. Thus, if F1, F2, and F3 are coordinatre frames, then vF1ToF2_F3 represents
* the vector from the origin of frame F1 to the origin of frame F2 represented in the frame F3.
*
* In this class, all vectors represent distances measured in feet.
*
* An angle in the 2-d plane can be defined with respect to a particular coordinate frame as the
* angle between the line of interest and the x-axis of the coordinate frame. An angle between
* a line of interest having name "ZZZ" and with respect to the x-axis of a frame F1 will be written
* as angZZZ_F1. Angles are assumed to be in degrees by default. If an angle is represented in units
* of radians (which is needed when the sin or cos of an angle is taken), then the text "Rad" will be
* added after the name of the line of interest. Thus the previous angle in radians would be
* angZZZRad_F1. Angles are assumed to increase in the counter-clockwise direction by default. If an
* An angle in the 2-d plane can be defined with respect to a particular coordinate frame as the
* angle between the line of interest and the x-axis of the coordinate frame. An angle between
* a line of interest having name "ZZZ" and with respect to the x-axis of a frame F1 will be written
* as angZZZ_F1. Angles are assumed to be in degrees by default. If an angle is represented in units
* of radians (which is needed when the sin or cos of an angle is taken), then the text "Rad" will be
* added after the name of the line of interest. Thus the previous angle in radians would be
* angZZZRad_F1. Angles are assumed to increase in the counter-clockwise direction by default. If an
* angle increases in the clockwise direction, "CWpos" will be added after the name and after the
* "Rad" text. In this case, if the original example is in units of radians and if the angle increases
* in the clockwise direction, it would be written as angZZZRadCWpos.
*
* An angle in the 2-d plane can also be defined as the angular measurement between the line of
* interest and a suitably chosen reference line. This will be written as angZZZWrtWWW where
* ZZZ is the name of the line of interest and WWW is the name of the reference line. Use of
* non-default units of degrees or a clockwise positive angle direction are represented as
* non-default units of degrees or a clockwise positive angle direction are represented as
* described above.
*
* The three coordinate frames of interest are given below. Note that these are planar frames and
* their height is not relevant for the model used in this class.
*
* Camera: Coordinate frame attached to camera with origin at focal point of lens, y axis
* pointing along the camera line of sight, and x axis pointing to the right when
* Camera: Coordinate frame attached to camera with origin at focal point of lens, y axis
* pointing along the camera line of sight, and x axis pointing to the right when
* looking from the back to the front of the camera.
*
* Robot: Coordinate frame attached to camera with origin at the centre of the robot base,
* y axis pointing toward the front along the centre line, and x axis pointing to
* Robot: Coordinate frame attached to camera with origin at the centre of the robot base,
* y axis pointing toward the front along the centre line, and x axis pointing to
* the right when looking from the back to the front of the robot.
*
* Field: Coordinate frame attached to field where the origin is in the lower left corner
* of the field, the y axis points down the field and the x axis points horizontally
* across the field. The home team is located at the bottom of the field and the
* drivers are looking down the field. The location of the origin is actually not
* Field: Coordinate frame attached to field where the origin is in the lower left corner
* of the field, the y axis points down the field and the x axis points horizontally
* across the field. The home team is located at the bottom of the field and the
* drivers are looking down the field. The location of the origin is actually not
* important since this frame is used only as a reference for the angular measurement.
*
* Two point of interest are
Expand All @@ -445,7 +445,7 @@ public void generateTrajectoryRobotToTarget(double distanceCameraToTarget_Camera
* at the centre of the ball.
*
* Final: Final position of robot as represented by the location of the origin of the robot
* frame. This is offset by a user-specified distance distance in the Config class
* frame. This is offset by a user-specified distance distance in the Config class
* given by TARGET_OFFSET_DISTANCE_<TARGET>, where <TARGET> is one of CARGO_AND_LOADING,
* ROCKET, or BALL.
*/
Expand Down
119 changes: 119 additions & 0 deletions src/main/java/ca/team2706/frc/robot/logging/Log.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
package ca.team2706.frc.robot.logging;

import ca.team2706.frc.robot.ConnectionState;
import ca.team2706.frc.robot.Robot;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Timer;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Properties;

/**
* Logs to USB and console at levels debug, info, warning, error
*/
public class Log {
private static final String LOG_FILE_KEY = "logFilename";
private static final Path LOG_LOCATION = Path.of("/U/logs");

private static boolean validDate;

static {
System.setProperty(LOG_FILE_KEY, logFile("latest"));
validDate = false;
}

private static final Logger LOGGER = LogManager.getLogger(Robot.class.getName());
private static final String BUILD_INFO_NAME = "/build-info.properties";
Expand All @@ -32,6 +51,106 @@ public static void init() {
Log.i("Game specific message: " + DriverStation.getInstance().getGameSpecificMessage());
}

/**
* Given the current date, formats the date for the start of the program
*
* @param secondsAgo The time in seconds since the program started
* @return The formatted start time of the program
*/
private static String formattedDate(double secondsAgo) {
return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(Date.from(Instant.now().minus((long) (secondsAgo * 1000), ChronoUnit.MILLIS)));
}

/**
* Sets up the logs for the newly connected driverstation and FMS
*
* @param state The new connection state
*/
public static void setupFMS(ConnectionState state) {
if (state == ConnectionState.FMS_CONNECT) {
String eventName = DriverStation.getInstance().getEventName();
String matchType = DriverStation.getInstance().getMatchType().name();
int matchNumber = DriverStation.getInstance().getMatchNumber();
int replayNumber = DriverStation.getInstance().getReplayNumber();
double matchTime = DriverStation.getInstance().getMatchTime();

Log.d("FMS: " + eventName + " " + matchType + " " + matchNumber + "-" + replayNumber + " at " + matchTime);

String logFile = logFile(eventName + "-" + matchType + "-" + matchNumber + "-" + replayNumber);

if (!logFile.equals(System.getProperty(LOG_FILE_KEY))) {
validDate = true;

changeLogFile(logFile);
}
} else if (state == ConnectionState.DRIVERSTATION_CONNECT && !validDate) {
String logFile = logFile(formattedDate(Timer.getFPGATimestamp()));
validDate = true;

changeLogFile(logFile);
}
}

/**
* Changes the log file to a new location
*
* @param newFile The new location
*/
private static void changeLogFile(String newFile) {
Log.i("Changed log file from " + System.getProperty(LOG_FILE_KEY) + " to " + newFile);

final Path oldPath = Paths.get(System.getProperty(LOG_FILE_KEY));

org.apache.logging.log4j.core.LoggerContext ctx =
(org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);

try {
Files.copy(Paths.get(System.getProperty(LOG_FILE_KEY)), Paths.get(newFile));
} catch (IOException e) {
e.printStackTrace();
}

System.setProperty(LOG_FILE_KEY, newFile);

ctx.reconfigure();

// File is still locked so periodically loop until it can be deleted
Thread delete = new Thread(() -> {
// Loop until file is deleted
while (Files.isRegularFile(oldPath)) {
try {
Files.delete(oldPath);
} catch (IOException ignored) {
}

try {
Thread.sleep(10);
} catch (InterruptedException e) {
return;
}
}
});
delete.setDaemon(true);
delete.start();
}

/**
* Gets the path to a log file from the name
*
* @param name The name of the file to log
* @return The path with a number at the end if the original alredy exists
*/
private static String logFile(String name) {
String fileName = LOG_LOCATION.resolve(name + ".log").toString();

int i = 1;
while (Files.exists(Path.of(fileName))) {
fileName = LOG_LOCATION.resolve(name + "-" + i++ + ".log").toString();
}

return fileName;
}

/**
* Logs program information
*/
Expand Down
Loading

0 comments on commit d015e07

Please sign in to comment.