-
Notifications
You must be signed in to change notification settings - Fork 45
Channel rewriter #781
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
Channel rewriter #781
Changes from all commits
5abe18b
c088cd2
63c5e76
5afae87
11c631c
9b47f5e
4e1d525
64a976c
7f80fc5
5e34415
b5be41b
26f0744
5eec73e
b8ceb74
9a452ec
4787e1d
d38df3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,17 +2,20 @@ | |
|
|
||
| import ev3dev.hardware.EV3DevDevice; | ||
| import ev3dev.hardware.EV3DevPlatform; | ||
| import ev3dev.utils.Sysfs; | ||
| import ev3dev.utils.DataChannelRewriter; | ||
| import lejos.hardware.LED; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import java.io.Closeable; | ||
| import java.io.IOException; | ||
|
|
||
| /** | ||
| * This class provides methods for interacting with the LEDs on the EV3Brick. | ||
| * | ||
| * <p><i>Only EV3Bricks are supported.</i> | ||
| */ | ||
| public class EV3Led extends EV3DevDevice implements LED { | ||
| public class EV3Led extends EV3DevDevice implements LED, Closeable { | ||
|
|
||
| /** | ||
| * Directions of the LED. | ||
|
|
@@ -21,16 +24,26 @@ public enum Direction { | |
| LEFT, | ||
| RIGHT | ||
| } | ||
|
|
||
| private static final Direction[] directionArray = {Direction.LEFT,Direction.RIGHT}; | ||
|
|
||
| private static final Logger log = LoggerFactory.getLogger(EV3Led.class); | ||
|
|
||
| /** | ||
| * @deprecated Use EV3LedDirection.LEFT instead. | ||
| */ | ||
| @Deprecated | ||
| public static final int LEFT = 0; | ||
| /** | ||
| * @deprecated Use EV3Led.Direction.RIGHT instead. | ||
| */ | ||
| @Deprecated | ||
| public static final int RIGHT = 1; | ||
|
|
||
| private final Direction direction; | ||
|
|
||
| private final String LED_RED; | ||
| private final String LED_GREEN; | ||
| private final DataChannelRewriter redWriter; | ||
| private final DataChannelRewriter greenWriter; | ||
|
|
||
| /** | ||
| * Create an EV3LED object associated with the LED of the specified direction. | ||
|
|
@@ -50,11 +63,11 @@ public EV3Led(final Direction direction) { | |
| this.direction = direction; | ||
|
|
||
| if (direction == Direction.LEFT) { | ||
| LED_RED = ev3DevProperties.getProperty("ev3.led.left.red"); | ||
| LED_GREEN = ev3DevProperties.getProperty("ev3.led.left.green"); | ||
| redWriter = new DataChannelRewriter(ev3DevProperties.getProperty("ev3.led.left.red")); | ||
| greenWriter = new DataChannelRewriter(ev3DevProperties.getProperty("ev3.led.left.green")); | ||
| } else { | ||
| LED_RED = ev3DevProperties.getProperty("ev3.led.right.red"); | ||
| LED_GREEN = ev3DevProperties.getProperty("ev3.led.right.green"); | ||
| redWriter = new DataChannelRewriter(ev3DevProperties.getProperty("ev3.led.right.red")); | ||
| greenWriter = new DataChannelRewriter(ev3DevProperties.getProperty("ev3.led.right.green")); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -65,22 +78,9 @@ public EV3Led(final Direction direction) { | |
| * @throws RuntimeException if LED feature is not supported on the current platform. | ||
| * @deprecated Use {@link #EV3Led(Direction)} instead. | ||
| */ | ||
| @Deprecated | ||
| public EV3Led(final int button) { | ||
| checkPlatform(); | ||
|
|
||
| if (button == LEFT) { | ||
| LED_RED = ev3DevProperties.getProperty("ev3.led.left.red"); | ||
| LED_GREEN = ev3DevProperties.getProperty("ev3.led.left.green"); | ||
| direction = Direction.LEFT; | ||
| } else if (button == RIGHT) { | ||
| LED_RED = ev3DevProperties.getProperty("ev3.led.right.red"); | ||
| LED_GREEN = ev3DevProperties.getProperty("ev3.led.right.green"); | ||
| direction = Direction.RIGHT; | ||
| } else { | ||
| log.error("You are not specifying any button."); | ||
| throw new IllegalArgumentException("You are not specifying any button."); | ||
| } | ||
|
|
||
| this(directionArray[button]); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -96,7 +96,6 @@ private void checkPlatform() { | |
| } | ||
|
|
||
| //TODO Add Enums for patterns | ||
|
|
||
| /** | ||
| * Sets the pattern of light to be shown with this LED. | ||
| * | ||
|
|
@@ -109,19 +108,22 @@ private void checkPlatform() { | |
| */ | ||
| @Override | ||
| public void setPattern(final int pattern) { | ||
| //Off | ||
|
|
||
| final String off = Integer.toString(0); | ||
| final String on = Integer.toString(255); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to change this, it might be worth it to also have only a single
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kind of want to let the kids play with the integer settings. But I think the best they can do is red + green = some kind of glowing brown. An ambitious kid could have the colors fade instead of change suddenly. That level of control is not for this fix.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant this only about the code style - there should be no problem with adjusting the integer value later. It's just that the code below this line that calls
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll give that a try tonight and see what it looks like. |
||
|
|
||
| if (pattern == 0) { | ||
| Sysfs.writeInteger(LED_RED, 0); | ||
| Sysfs.writeInteger(LED_GREEN, 0); | ||
| greenWriter.writeString(off); | ||
| redWriter.writeString(off); | ||
| } else if (pattern == 1) { | ||
| Sysfs.writeInteger(LED_RED, 0); | ||
| Sysfs.writeInteger(LED_GREEN, 255); | ||
| greenWriter.writeString(on); | ||
| redWriter.writeString(off); | ||
| } else if (pattern == 2) { | ||
| Sysfs.writeInteger(LED_RED, 255); | ||
| Sysfs.writeInteger(LED_GREEN, 0); | ||
| greenWriter.writeString(off); | ||
| redWriter.writeString(on); | ||
| } else if (pattern == 3) { | ||
| Sysfs.writeInteger(LED_RED, 255); | ||
| Sysfs.writeInteger(LED_GREEN, 255); | ||
| greenWriter.writeString(on); | ||
| redWriter.writeString(on); | ||
| } else if (pattern > 3) { | ||
| log.debug("This feature is not implemented"); | ||
| } | ||
|
|
@@ -135,4 +137,12 @@ public void setPattern(final int pattern) { | |
| public Direction getDirection() { | ||
| return direction; | ||
| } | ||
|
|
||
| @Override | ||
| public void close() throws IOException { | ||
| greenWriter.close(); | ||
| redWriter.close(); | ||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,38 +67,37 @@ private Battery() { | |
| currentRereader = new DataChannelRereader(batteryPath + "/" + batteryEv3 + "/" + current); | ||
| } | ||
|
|
||
| public int getVoltageMicroVolts() { | ||
| public int getVoltageMicroVolt() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This changes public API
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, nevermind, this was likely accepted in the previous pull request.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed the new method name to match the existing unit postfix. |
||
| return Integer.parseInt(voltageRereader.readString()); | ||
| } | ||
|
|
||
| /** | ||
| * @return voltage of the battery in millivolts. | ||
| */ | ||
| @Override | ||
| public int getVoltageMilliVolt() { | ||
| return getVoltageMicroVolts() / 1000; | ||
| return getVoltageMicroVolt() / 1000; | ||
| } | ||
|
|
||
| /** | ||
| * Returns voltage of the battery in microvolts. | ||
| * | ||
| * @return voltage | ||
| * @return voltage of the battery in microvolts. | ||
| */ | ||
| public float getVoltage() { | ||
| return getVoltageMicroVolts() / 1000000f; | ||
| return getVoltageMicroVolt() / 1000000f; | ||
| } | ||
|
|
||
| //TODO Review output | ||
| //TODO Review units | ||
|
|
||
| /** | ||
| * Returns the current of the battery in amps. | ||
| * | ||
| * @return current | ||
| * @return current from the battery in amps, or Float.NaN if run on something other than EV3BRICK | ||
| */ | ||
| public float getBatteryCurrent() { | ||
| if (CURRENT_PLATFORM.equals(EV3DevPlatform.EV3BRICK)) { | ||
| return Float.parseFloat(currentRereader.readString()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unrelated to DataChannelRereader/DataChannelRewriter and as such it should be addressed in a different PR, but this method likely contains a bug. I'm wondering if this applies universally: if you're using The problem is that the value will be read in microamps instead of amps. As per https://github.com/torvalds/linux/blob/master/Documentation/power/power_supply_class.rst:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that going to be a problem for sensor's GenericMode? It's filling arrays of floats. The gyroscope only returns ints. I think the color sensor's API says it gives values between 0 and 1, but I haven't plugged ours in to verify that yet.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GenericMode is luckily aware of the scaling, so while it reads floats from sysfs, it multiplies them by a scaling factor to get results in proper SI units. It should be possible to put read-integer there without breaking anything.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's an undercurrent of intent in the code to replace GenericMode with more specific modes. Maybe that's some future work to take up. Floating point is pretty easy to start with, but turns out to be remarkably hard to explain to kids why == is a bad choice. Rational numbers in contrast are easy. With really small units - like millimeter, millisecond, or microvolt - it is really easy to explain why == is a bad choice. The real twist with GenericMode was "If I only want the number of degrees why do I have to pass in a float array of one float?" |
||
| } else { | ||
| LOGGER.warn("This method is not available for {} & {}", EV3DevPlatform.PISTORMS, EV3DevPlatform.BRICKPI); | ||
| return -1f; | ||
| return Float.NaN; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| package ev3dev.utils; | ||
|
|
||
| import java.io.Closeable; | ||
| import java.io.IOException; | ||
| import java.nio.ByteBuffer; | ||
| import java.nio.channels.FileChannel; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.nio.file.StandardOpenOption; | ||
|
|
||
| /** | ||
| * Writer of streams that can rewrite the same channel for structured data of | ||
| * known length. The focus of this class is on performance. | ||
| * | ||
| * @author David Walend | ||
| */ | ||
| public class DataChannelRewriter implements Closeable { | ||
|
|
||
| private final Path path; | ||
| private final ByteBuffer byteBuffer; | ||
| private final FileChannel channel; | ||
|
|
||
| /** | ||
| * Create a DataChannelRewriter for path with a bufferLength byte buffer | ||
| * | ||
| * @param path path to the file to reread | ||
| * @param bufferLength length of the buffer to hold the structure | ||
| */ | ||
| public DataChannelRewriter(Path path, int bufferLength) { | ||
| this.path = path; | ||
| this.byteBuffer = ByteBuffer.allocate(bufferLength); | ||
| try { | ||
| this.channel = FileChannel.open(path, StandardOpenOption.WRITE); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not enabled by default. I had to use a different codepath to create the files in the test. (It does give a very nice message if it can't create a file.) I'd like to leave it out until we actually need it. Are there any cases in ev3dev where we'd want DataChannelRewriter to create a new file - to control something in ev3dev? We'd also want to rename the class - it wouldn't be rewriting a new file.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not in ev3dev - it should be fine there. However, some sensor unit tests may be not be creating all attributes that the class uses. I agree that it might be better to just fix the tests instead of changing the code here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really need to get some background on how the existing tests work. It's not what I thought I would find - via a properties change to set up a file system that looks like the ev3dev's, and I'm having trouble seeing through the varied test styles into the setup code. |
||
| } catch (IOException e) { | ||
| throw new RuntimeException("While opening " + path,e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Create a DataChannelRewriter for pathString with the default 32-byte buffer. | ||
| * | ||
| * @param pathString Path to the file to reread | ||
| */ | ||
| public DataChannelRewriter(String pathString) { | ||
| this(Paths.get(pathString),32); | ||
| } | ||
|
|
||
| /** | ||
| * @param string to write. A new line character | ||
| */ | ||
| public synchronized void writeString(String string) { | ||
| try { | ||
| byteBuffer.clear(); | ||
| byteBuffer.put(string.getBytes(StandardCharsets.UTF_8)); | ||
| byteBuffer.put(((byte)'\n')); | ||
| byteBuffer.flip(); | ||
| channel.truncate(0); | ||
| channel.write(byteBuffer,0); | ||
| channel.force(false); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Problem writing path: " + path, e); | ||
| } | ||
| } | ||
|
|
||
| public Path getPath() { | ||
| return path; | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized void close() throws IOException { | ||
| channel.close(); | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized here that some attributes may not be read-only or write-only. For example, the
duty_cycle_spattribute is both written and read inBaseRegulatedMotor. This requires having two separate objects for the reads & writes (that might be OK though).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only prototyped BaseRegulatedMotor so far. Are there any conditions where we'd want read and write in the same interaction? Or where they'd interfere with each other? We could build something like a random access file if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC they will not interfere with each other, so having two objects, one for reading and the other for writing, should work fine. I just got surprised that we are going to have two separate objects for one "real" object/sysfs attribute.