Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype for event and metric logging plus start of basic executor system #77

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions RobotFramework/src/org/frc4931/acommand/Command.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* FRC 4931 (http://www.evilletech.com)
*
* Open source software. Licensed under the FIRST BSD license file in the
* root directory of this project's Git repository.
*/
package org.frc4931.acommand;

/**
* The public interface for a command.
*/
public interface Command {

CommandID id();

}
88 changes: 88 additions & 0 deletions RobotFramework/src/org/frc4931/acommand/CommandID.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* FRC 4931 (http://www.evilletech.com)
*
* Open source software. Licensed under the FIRST BSD license file in the
* root directory of this project's Git repository.
*/
package org.frc4931.acommand;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
* A immuntable unique identifier for a command.
*/
public final class CommandID implements Comparable<CommandID>{

private static final Map<Class<Command>,CommandID> COMMANDS = new HashMap<>();
private static final AtomicInteger ID = new AtomicInteger();

/**
* Get the identifier for the given command class with optional name.
* @param clazz the {@link Command} class; may not be null
* @param name the human-readable name of the class; if null, then the class' {@link Class#getSimpleName() simple name} will be used
* @return the identifier for the command; never null
*/
public static synchronized CommandID get( Class<Command> clazz, String name ) {
CommandID id = COMMANDS.get(clazz);
if ( id == null ) {
if ( clazz == null ) throw new IllegalArgumentException("The 'clazz' argument may not be null");
id = new CommandID(clazz,ID.incrementAndGet(),name);
COMMANDS.put(clazz,id);
}
return id;
}

private final Class<Command> commandClass;
private final int id;
private final String name;

private CommandID(Class<Command> clazz, int id, String name) {
this.commandClass = clazz;
this.name = name;
this.id = id;
}

/**
* The the name of the command.
* @return the command's name; never null
*/
public String getName() {
return name != null ? name : commandClass.getSimpleName();
}

/**
* Get the unique numeric identifier for this command.
* @return the numeric identifier
*/
public int asInt() {
return id;
}

@Override
public int hashCode() {
return id;
}

@Override
public boolean equals(Object obj) {
if ( obj instanceof CommandID ) {
return ((CommandID)obj).id == this.id;
}
return false;
}

@Override
public String toString() {
return getName() + " (" + id + ")";
}

@Override
public int compareTo(CommandID that) {
if ( this == that ) return 0;
if ( that == null ) return 1;
return this.id - that.id;
}

}
16 changes: 16 additions & 0 deletions RobotFramework/src/org/frc4931/acommand/CommandState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* FRC 4931 (http://www.evilletech.com)
*
* Open source software. Licensed under the FIRST BSD license file in the
* root directory of this project's Git repository.
*/
package org.frc4931.acommand;

/**
* The possible states of a command.
*/
public enum CommandState {

STARTED, RUNNING, COMPLETED, INTERRUPTED

}
162 changes: 162 additions & 0 deletions RobotFramework/src/org/frc4931/executor/EventLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* FRC 4931 (http://www.evilletech.com)
*
* Open source software. Licensed under the FIRST BSD license file in the
* root directory of this project's Git repository.
*/
package org.frc4931.executor;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.StringWriter;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

import org.frc4931.acommand.CommandID;
import org.frc4931.acommand.CommandState;

/**
*
*/
public class EventLog implements AutoCloseable {

private static final byte VERSION = 1;

private static final byte HEADER = 1;
private static final byte SWITCH_EVENT = 2;
private static final byte MESSAGE_EVENT = 3;
private static final byte EXCEPTION_EVENT = 4;
private static final byte COMMAND_EVENT = 5;
private static final byte COMMAND_TYPE = 6;

private final File outFile;
private final FileChannel channel;
private final MappedByteBuffer buffer;
private final long capacity;
private final Map<Integer, CommandID> commandsById = new HashMap<>();

/**
* Constructs a new {@link MappedWriter} to write to the file at the specified path.
*
* @param pathSupplier the supplier of the {@link Path path} that references the file to which this writer will write; may not
* be null
* @param size the maximum number of bytes that can be written
* @param startTime the start time of the match
* @throws IOException if an I/O error occurs
*/
public EventLog(Supplier<Path> pathSupplier, long size, LocalDateTime startTime) throws IOException {
outFile = pathSupplier.get().toFile();
channel = new RandomAccessFile(outFile, "rw").getChannel();
capacity = size;
buffer = channel.map(MapMode.READ_WRITE, 0, capacity);
// Write the header ...
writeHeader(startTime);
}

private void writeHeader(LocalDateTime startTime) {
buffer.put(HEADER);
// Write the version of this format ...
buffer.put(VERSION);
// Write the starting date in seconds since epoch ...
buffer.putLong(startTime.toEpochSecond(ZonedDateTime.now().getOffset()));
}

private void writeString(String str) {
if (str.isEmpty()) {
buffer.putShort((short) 0);
} else {
buffer.putShort((short) str.length());
buffer.put(str.getBytes(StandardCharsets.UTF_8));
}
}

// private String readString() {
// int len = buffer.getShort();
// if ( len == 0 ) return "";
// byte[] bytes = new byte[len];
// buffer.get(bytes, 0, len);
// return new String(bytes, 0, len);
// }

private void writeBoolean(boolean value) {
buffer.put((byte) (value ? 1 : 0));
}

// private boolean readBoolean() {
// return buffer.get() == 1;
// }

public void writeChangeInSwitch(int elapsedTimeInMillis, String switchName, boolean currentState) {
buffer.put(SWITCH_EVENT);
buffer.putInt(elapsedTimeInMillis);
writeString(switchName);
writeBoolean(currentState);
}

public void writeMessage(int elapsedTimeInMillis, String message, Throwable error) {
if (error == null) {
buffer.put(MESSAGE_EVENT);
buffer.putInt(elapsedTimeInMillis);
writeString(message);
} else {
buffer.put(EXCEPTION_EVENT);
buffer.putInt(elapsedTimeInMillis);
writeString(message);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
error.printStackTrace(pw);
writeString(sw.toString()); // stack trace as a string
}
}

public void writeChangeInCommandState(int elapsedTimeInMillis, CommandID commandId, CommandState state) {
if (!commandsById.containsKey(commandId.asInt())) {
commandsById.put(commandId.asInt(), commandId);
buffer.put(COMMAND_TYPE);
buffer.putInt(commandId.asInt());
writeString(commandId.getName());
}
buffer.put(COMMAND_EVENT);
buffer.putInt(elapsedTimeInMillis);
buffer.putInt(commandId.asInt());
buffer.put((byte) state.ordinal());
}

public int remaining() {
return buffer.remaining();
}

/**
* Writes the terminator, forces the OS to update the file
* and closes the {@code Channel}.
*/
@Override
public void close(){
try {
// Write terminator
buffer.putInt(0xFFFFFFFF);
} finally {
try {
// And always force the buffer ...
buffer.force();
} finally {
try{
// And always close the channel ...
channel.close();
} catch (IOException e) {
throw new RuntimeException("Failed to close channel",e);
}
}
}
}
}
Loading