Skip to content
Merged
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
8 changes: 6 additions & 2 deletions playbooks/files/defaultConfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ launch:
serialPort: /dev/ttyACM0
baudRate: 115200
heartbeatRate: 1
fileReceiverPort: 12347
fileReceiverSocketBacklog: 100
topsides:
pilotPanelName: Pilot Panel
pilotPanelPort: /dev/ttyACM0
Expand Down Expand Up @@ -92,8 +94,10 @@ cameraCalibration:
cameraBImagesDirectory: /home/eedge/calibration-b/
cameraAValidImagesDirectory: /home/eedge/valid-calibration-a/
cameraBValidImagesDirectory: /home/eedge/valid-calibration-b/
chessboardWidth: 12
chessboardHeight: 13
cameraAPreUndistortedDirectory: /home/eedge/pre-undistorted-a/
cameraBPreUndistortedDirectory: /home/eedge/pre-undistorted-b/
chessboardWidth: 6
chessboardHeight: 9
distanceCalculator:
imageDirectory: /home/eedge/undistorted-images/
dataDirectory: /home/eedge/undistorted-images-data/
2 changes: 1 addition & 1 deletion playbooks/files/etc/systemd/system/eer-camera.service
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ Description=Eastern Edge Robotics Camera Feed Software

[Service]
EnvironmentFile=/run/eer/camera-environment
ExecStart=/usr/bin/eer-camera-feed $host $port
ExecStart=/usr/bin/eer-camera-feed $host $video_port $image_port
46 changes: 36 additions & 10 deletions playbooks/files/usr/bin/eer-camera-feed
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,53 @@ import picamera
import signal
import socket
import sys
import threading

host = sys.argv[1]
video_port = int(sys.argv[2])
image_port = int(sys.argv[3])

signal_lock = threading.Lock()

with picamera.PiCamera() as camera:
def flip_video(signum, stack):
camera.vflip = not camera.vflip
camera.hflip = not camera.hflip
with signal_lock:
camera.vflip = not camera.vflip
camera.hflip = not camera.hflip
signal.signal(signal.SIGUSR1, flip_video)
camera.resolution = (1296, 720)

def take_image(signum, stack):
with signal_lock:
with open('/run/eer/camera-image-path', 'r') as image_path_file:
image_path = image_path_file.readlines()[0]
image_socket = socket.socket()
image_socket.connect((host, image_port))
image_socket.send(image_path + '\n')
image_file = image_socket.makefile('wb')
try:
camera.capture(image_file, 'png')
finally:
image_file.close()
image_socket.close()

signal.signal(signal.SIGUSR2, take_image)

camera.resolution = (1920, 1088)
camera.framerate = 24

nc = socket.socket()
nc.connect((sys.argv[1], int(sys.argv[2])))
fd = nc.makefile('wb')
video_socket = socket.socket()
video_socket.connect((host, video_port))
video_file = video_socket.makefile('wb', 1024)
try:
camera.start_recording(
fd,
video_file,
format='h264',
intra_period=0,
quality=0,
bitrate=25000000)
bitrate=25000000,
resize=(1296, 720))
while True:
signal.pause()
finally:
fd.close()
nc.close()
video_file.close()
video_socket.close()
22 changes: 18 additions & 4 deletions src/main/java/com/easternedgerobotics/rov/PicameraA.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.easternedgerobotics.rov.config.LaunchConfig;
import com.easternedgerobotics.rov.event.BroadcastEventPublisher;
import com.easternedgerobotics.rov.event.EventPublisher;
import com.easternedgerobotics.rov.value.CameraCaptureValueA;
import com.easternedgerobotics.rov.value.PicameraAHeartbeatValue;
import com.easternedgerobotics.rov.value.VideoFlipValueA;
import com.easternedgerobotics.rov.value.VideoValueA;
Expand Down Expand Up @@ -33,14 +34,27 @@ private PicameraA() {

}

private static void initCameraA(final EventPublisher eventPublisher, final int heartbeatRate) {
private static void initCameraA(
final EventPublisher eventPublisher,
final int heartbeatRate,
final int fileReceiverPort
) {
eventPublisher.valuesOfType(VideoFlipValueA.class)
.observeOn(Schedulers.io())
.subscribe(f -> PicameraVideo.flip());

eventPublisher.valuesOfType(CameraCaptureValueA.class)
.observeOn(Schedulers.io())
.subscribe(capture -> PicameraVideo.takeImage(capture.getPath()));

eventPublisher.valuesOfType(VideoValueA.class)
.throttleLast(1, TimeUnit.SECONDS)
.subscribe(value -> PicameraVideo.start(value.getHost(), value.getPort()));
.throttleLast(1, TimeUnit.SECONDS, Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(value -> PicameraVideo.start(value.getHost(), value.getPort(), fileReceiverPort));

Observable.interval(heartbeatRate, TimeUnit.SECONDS, Schedulers.io())
.map(t -> new PicameraAHeartbeatValue(true))
.observeOn(Schedulers.io())
.subscribe(eventPublisher::emit);
}

Expand Down Expand Up @@ -81,7 +95,7 @@ public static void main(final String[] args) throws InterruptedException, Socket
final EventPublisher eventPublisher = new BroadcastEventPublisher(new UdpBroadcast<>(
socket, broadcastAddress, broadcastPort, new BasicOrder<>()));
PicameraVideo.addShutdownHook();
initCameraA(eventPublisher, launchConfig.heartbeatRate());
initCameraA(eventPublisher, launchConfig.heartbeatRate(), launchConfig.fileReceiverPort());
Logger.info("Started");
eventPublisher.await();
} catch (final ParseException e) {
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/com/easternedgerobotics/rov/PicameraB.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.easternedgerobotics.rov.config.LaunchConfig;
import com.easternedgerobotics.rov.event.BroadcastEventPublisher;
import com.easternedgerobotics.rov.event.EventPublisher;
import com.easternedgerobotics.rov.value.CameraCaptureValueB;
import com.easternedgerobotics.rov.value.PicameraBHeartbeatValue;
import com.easternedgerobotics.rov.value.VideoFlipValueB;
import com.easternedgerobotics.rov.value.VideoValueB;
Expand Down Expand Up @@ -33,14 +34,27 @@ private PicameraB() {

}

private static void initCameraB(final EventPublisher eventPublisher, final int heartbeatRate) {
private static void initCameraB(
final EventPublisher eventPublisher,
final int heartbeatRate,
final int fileReceiverPort
) {
eventPublisher.valuesOfType(VideoFlipValueB.class)
.observeOn(Schedulers.io())
.subscribe(f -> PicameraVideo.flip());

eventPublisher.valuesOfType(CameraCaptureValueB.class)
.observeOn(Schedulers.io())
.subscribe(capture -> PicameraVideo.takeImage(capture.getPath()));

eventPublisher.valuesOfType(VideoValueB.class)
.throttleLast(1, TimeUnit.SECONDS)
.subscribe(value -> PicameraVideo.start(value.getHost(), value.getPort()));
.throttleLast(1, TimeUnit.SECONDS, Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(value -> PicameraVideo.start(value.getHost(), value.getPort(), fileReceiverPort));

Observable.interval(heartbeatRate, TimeUnit.SECONDS, Schedulers.io())
.map(t -> new PicameraBHeartbeatValue(true))
.observeOn(Schedulers.io())
.subscribe(eventPublisher::emit);
}

Expand Down Expand Up @@ -81,7 +95,7 @@ public static void main(final String[] args) throws InterruptedException, Socket
final EventPublisher eventPublisher = new BroadcastEventPublisher(new UdpBroadcast<>(
socket, broadcastAddress, broadcastPort, new BasicOrder<>()));
PicameraVideo.addShutdownHook();
initCameraB(eventPublisher, launchConfig.heartbeatRate());
initCameraB(eventPublisher, launchConfig.heartbeatRate(), launchConfig.fileReceiverPort());
Logger.info("Started");
eventPublisher.await();
} catch (final ParseException e) {
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/com/easternedgerobotics/rov/Topside.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.easternedgerobotics.rov.io.MotionPowerProfile;
import com.easternedgerobotics.rov.io.ProfileController;
import com.easternedgerobotics.rov.io.SliderController;
import com.easternedgerobotics.rov.io.TcpFileReceiver;
import com.easternedgerobotics.rov.io.arduino.Arduino;
import com.easternedgerobotics.rov.io.arduino.ArduinoPort;
import com.easternedgerobotics.rov.io.joystick.JoystickController;
Expand Down Expand Up @@ -60,6 +61,8 @@ public final class Topside extends Application {

private JoystickController joystickController;

private TcpFileReceiver fileReceiver;

@Override
public void init() throws SocketException, UnknownHostException {
final Config configSource = new Config(
Expand Down Expand Up @@ -104,10 +107,12 @@ public void init() throws SocketException, UnknownHostException {
eventPublisher, configSource.getConfig("videoDecoder", VideoDecoderConfig.class));

cameraCalibration = new CameraCalibration(
videoDecoder,
eventPublisher,
configSource.getConfig("cameraCalibration", CameraCalibrationConfig.class),
Schedulers.newThread());

fileReceiver = new TcpFileReceiver(launchConfig.fileReceiverPort(), launchConfig.fileReceiverSocketBacklog());

viewLoader = new ViewLoader(MainView.class, "Control Software", new HashMap<Class<?>, Object>() {
{
put(EventPublisher.class, eventPublisher);
Expand All @@ -134,6 +139,7 @@ public void start(final Stage stage) {
arduino.start(config.pilotPanelHeartbeatInterval(), config.pilotPanelHeartbeatTimeout(), TimeUnit.MILLISECONDS);
joystickController.start(LogitechExtremeJoystickSource.create(
config.joystickRecoveryInterval(), TimeUnit.MILLISECONDS, Schedulers.io()));
fileReceiver.start();
Logger.info("Started");
}

Expand All @@ -146,6 +152,7 @@ public void stop() {
profileController.stop();
videoDecoder.stop();
joystickController.stop();
fileReceiver.stop();
Logger.info("Stopped");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public interface CameraCalibrationConfig {

String cameraBValidImagesDirectory();

String cameraAPreUndistortedDirectory();

String cameraBPreUndistortedDirectory();

int chessboardWidth();

int chessboardHeight();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ public interface LaunchConfig {
int baudRate();

int heartbeatRate();

int fileReceiverPort();

int fileReceiverSocketBacklog();
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ private void updateDirectory(final ObservableValue<? extends String> value, fina
subscriptions.clear();
subscriptions.add(DirectoryUtil.observe(Paths.get(curr))
.subscribeOn(Schedulers.newThread())
.delay(1, TimeUnit.SECONDS)
.subscribe(this::onImagePathChanged));
}

Expand All @@ -84,6 +83,10 @@ private void onImagePathChanged(final Path path) {
});
return;
}
// in the event that the file was not completed.
if (file.length() == 0) {
Schedulers.io().createWorker().schedule(() -> onImagePathChanged(path), 1, TimeUnit.SECONDS);
}
try {
final GalleryImageView iv = new GalleryImageView(path, view.actions);
Platform.runLater(() -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package com.easternedgerobotics.rov.fx;

import com.easternedgerobotics.rov.config.Configurable;
import com.easternedgerobotics.rov.control.SourceController;
import com.easternedgerobotics.rov.event.EventPublisher;
import com.easternedgerobotics.rov.io.EmergencyStopController;
import com.easternedgerobotics.rov.value.HeartbeatValue;
import com.easternedgerobotics.rov.value.PicameraAHeartbeatValue;
import com.easternedgerobotics.rov.value.PicameraBHeartbeatValue;
import com.easternedgerobotics.rov.value.RasprimeHeartbeatValue;
import com.easternedgerobotics.rov.value.TopsideHeartbeatValue;
import com.easternedgerobotics.rov.video.VideoDecoder;

import javafx.scene.control.ToggleButton;
import rx.Observable;
import rx.Subscription;
import rx.observables.JavaFxObservable;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;

Expand Down Expand Up @@ -73,19 +74,17 @@ public final void onCreate() {
final Observable<Long> heartbeatLostInterval = Observable.interval(
maxHeartbeatGap, TimeUnit.SECONDS, JAVA_FX_SCHEDULER);

Observable.zip(
Observable.from(Arrays.asList(
RasprimeHeartbeatValue.class,
PicameraAHeartbeatValue.class,
PicameraBHeartbeatValue.class)),
Observable.from(Arrays.asList(
view.rasprimeIndicator,
view.picameraAIndicator,
view.picameraBIndicator)),
(clazz, indicator) -> SourceController.manageMultiViewModel(
eventPublisher.valuesOfType(clazz), h -> indicator.setBackground(MainView.FOUND_BG), JAVA_FX_SCHEDULER,
heartbeatLostInterval, t -> indicator.setBackground(MainView.LOST_BG), JAVA_FX_SCHEDULER)
).subscribe(subscriptions::add);
final Observable<HeartbeatValue> rasprimeHeartbeats = eventPublisher
.valuesOfType(RasprimeHeartbeatValue.class).cast(HeartbeatValue.class);
final Observable<HeartbeatValue> picameraAHeartbeats = eventPublisher
.valuesOfType(PicameraAHeartbeatValue.class).cast(HeartbeatValue.class);
final Observable<HeartbeatValue> picameraBHeartbeats = eventPublisher
.valuesOfType(PicameraBHeartbeatValue.class).cast(HeartbeatValue.class);

subscriptions.addAll(
setIndicator(rasprimeHeartbeats, view.rasprimeIndicator),
setIndicator(picameraAHeartbeats, view.picameraAIndicator),
setIndicator(picameraBHeartbeats, view.picameraBIndicator));

JavaFxObservable.valuesOf(view.thrusterButton.pressedProperty()).filter(x -> !x)
.subscribe(v -> viewLoader.load(ThrusterPowerSlidersView.class, "Thruster Power"));
Expand All @@ -107,6 +106,15 @@ public final void onDestroy() {
eventPublisher.emit(new TopsideHeartbeatValue(false));
}

private Subscription setIndicator(final Observable<HeartbeatValue> heartbeats, final ToggleButton indicator) {
final Observable<Boolean> timeout = Observable.just(false)
.delay(maxHeartbeatGap, TimeUnit.SECONDS, JAVA_FX_SCHEDULER)
.concatWith(Observable.never());
return new CompositeSubscription(
timeout.takeUntil(heartbeats).repeat().subscribe(h -> indicator.setBackground(MainView.LOST_BG)),
heartbeats.subscribe(h -> indicator.setBackground(MainView.FOUND_BG)));
}

private void onSelected(final boolean selected) {
if (selected) {
view.startButton.setText("Stop");
Expand Down
Loading