Skip to content

Commit

Permalink
Documented
Browse files Browse the repository at this point in the history
  • Loading branch information
Levi-Lesches committed Jan 30, 2024
1 parent d6f2c2f commit 202827d
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 28 deletions.
2 changes: 1 addition & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ linter:
cascade_invocations: false # Cascades are less readable

# Temporarily disabled until we are ready to document
public_member_api_docs: false
# public_member_api_docs: false
37 changes: 21 additions & 16 deletions lib/src/isolates/child.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@ import "package:opencv_ffi/opencv_ffi.dart";

import "package:video/video.dart";

const maxPacketLength = 60000; // max UDP packet size in bytes

extension on CameraDetails {
bool get interferesWithAutonomy => hasResolutionHeight()
|| hasResolutionWidth()
|| hasFps()
|| hasStatus();
}
/// The maximum size of a UDP packet, in bytes (minus a few to be safe).
const maxPacketLength = 60000;

/// A child isolate that manages a single camera and streams frames from it.
abstract class CameraIsolate extends IsolateChild<IsolatePayload, VideoCommand> {
/// Holds the current details of the camera.
final CameraDetails details;
/// A constructor with initial details.
CameraIsolate({required this.details}) : super(id: details.name);

/// A timer to periodically send the camera status to the dashboard.
Expand Down Expand Up @@ -51,13 +47,7 @@ abstract class CameraIsolate extends IsolateChild<IsolatePayload, VideoCommand>
}

@override
void onData(VideoCommand data) {
if (data.details.interferesWithAutonomy) {
sendLog(LogLevel.error, "That would break autonomy");
} else {
updateDetails(data.details);
}
}
void onData(VideoCommand data) => updateDetails(data.details);

/// Updates the camera's [details], which will take effect on the next [sendFrame] call.
void updateDetails(CameraDetails newDetails) {
Expand All @@ -66,21 +56,36 @@ abstract class CameraIsolate extends IsolateChild<IsolatePayload, VideoCommand>
start();
}

/// Disposes of this camera and all other resources.
///
/// After running this, the camera should need to be opened again.
void dispose() {
disposeCamera();
frameTimer?.cancel();
fpsTimer?.cancel();
statusTimer?.cancel();
}

/// Initializes the camera and starts streaming.
void initCamera();

/// Closes and releases the camera.
///
/// This is separate from [dispose] so the isolate can keep reporting its status.
void disposeCamera();

/// Reads frame/s from the camera and sends it/them.
void sendFrames();

/// Sends an individual frame to the dashboard.
///
/// This function also checks if the frame is too big to send, and if so,
/// lowers the JPG quality by 1%. If the quality reaches 25% (visually noticeable),
/// an error is logged instead.
void sendFrame(OpenCVImage image, {CameraDetails? detailsOverride}) {
final details = detailsOverride ?? this.details;
if (image.data.length < maxPacketLength) { // Frame can be sent
send(FramePayload(details: details, address: image.pointer.address, length: image.data.length));
send(FramePayload(details: details, image: image));
} else if (details.quality > 25) { // Frame too large, lower quality
sendLog(LogLevel.debug, "Lowering quality for $name from ${details.quality}");
details.quality -= 1; // maybe next frame can send
Expand Down
2 changes: 1 addition & 1 deletion lib/src/isolates/parent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class VideoController extends IsolateParent<VideoCommand, IsolatePayload>{
case DetailsPayload():
collection.videoServer.sendMessage(VideoData(details: data.details));
case FramePayload():
final frame = data.getFrame();
final frame = data.frame;
collection.videoServer.sendMessage(VideoData(frame: frame.data, details: data.details));
frame.dispose();
case DepthFramePayload():
Expand Down
18 changes: 15 additions & 3 deletions lib/src/isolates/payload.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,18 @@ class FramePayload extends IsolatePayload {
final int length;

/// A const constructor.
const FramePayload({required this.details, required this.address, required this.length});
FramePayload({required this.details, required OpenCVImage image}) :
address = image.pointer.address,
length = image.data.length;

/// The underlying data held at [address].
///
/// This cannot be a normal field as [Pointer]s cannot be sent across isolates, and this should
/// not be a getter because the underlying memory needs to be freed and cannot be used again.
OpenCVImage getFrame() => OpenCVImage(pointer: Pointer.fromAddress(address), length: length);
OpenCVImage get frame => OpenCVImage(pointer: Pointer.fromAddress(address), length: length);

/// Frees the data in this frame.
void dispose() => frame.dispose();
}

/// A class to send log messages across isolates. The parent isolate is responsible for logging.
Expand All @@ -54,10 +59,17 @@ class LogPayload extends IsolatePayload {
const LogPayload({required this.level, required this.message});
}

/// A depth frame to be sent to the Autonomy program.
class DepthFramePayload extends IsolatePayload {
/// The address of the data in memory, since pointers cannot be sent across isolates.
final int address;
const DepthFramePayload(this.address);
/// Saves the address of the pointer to send across isolates.
DepthFramePayload(Pointer<NativeFrames> pointer) :
address = pointer.address;

/// The native frame being referenced by this pointer.
Pointer<NativeFrames> get frame => Pointer<NativeFrames>.fromAddress(address);

/// Frees the memory associated with the frame.
void dispose() => frame.dispose();
}
26 changes: 25 additions & 1 deletion lib/src/isolates/realsense.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,35 @@ import "package:opencv_ffi/opencv_ffi.dart";

import "package:video/video.dart";

extension on CameraDetails {
bool get interferesWithAutonomy => hasResolutionHeight()
|| hasResolutionWidth()
|| hasFps()
|| hasStatus();
}

/// An isolate to read RGB, depth, and colorized frames from the RealSense.
///
/// While using the RealSense SDK for depth streaming, OpenCV cannot access the standard RGB frames,
/// so it is necessary for this isolate to grab the RGB frames as well.
///
/// Since the RealSense is being used for autonomy, certain settings that could interfere with the
/// autonomy program are not allowed to be changed, even for the RGB camera.
class RealSenseIsolate extends CameraIsolate {
/// The native RealSense object. MUST be `late` so it isn't initialized on the parent isolate.
late final RealSenseInterface camera = RealSenseInterface.forPlatform();
bool hasError = false;
/// Creates an isolate to read from the RealSense camera.
RealSenseIsolate({required super.details});

@override
void onData(VideoCommand data) {
if (data.details.interferesWithAutonomy) {
sendLog(LogLevel.error, "That would break autonomy");
} else {
super.onData(data);
}
}

@override
void initCamera() {
if (!camera.init()) {
Expand Down
11 changes: 6 additions & 5 deletions lib/src/realsense/ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import "dart:typed_data";
import "../generated/librealsense_ffi_bindings.dart";
export "../generated/librealsense_ffi_bindings.dart";

/// Utils on a [Pointer] to [NativeFrames].
extension NativeFramesUtils on Pointer<NativeFrames> {
/// Frees the memory of all the frames.
void dispose() => realsenseLib.NativeFrames_free(this);

/// The depth frame, as raw bytes.
///
/// NOTE: The RealSense SDK returns [Uint16]s, but this is cast to a [Uint8] for UDP transfer. Be
/// sure to re-cast it on the processing side!
Uint8List get depthFrame {
final NativeFrames struct = ref;
return struct.depth_data.asTypedList(struct.depth_length);
}

Uint8List get colorizedFrame {
final NativeFrames struct = ref;
return struct.colorized_data.asTypedList(struct.colorized_length);
}
}

String _getPath() {
Expand Down
14 changes: 13 additions & 1 deletion lib/src/realsense/interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,32 @@ import "package:video/video.dart";
import "realsense_ffi.dart";
import "realsense_stub.dart";

/// An interface for reading the RealSense camera.
abstract class RealSenseInterface {
RealSenseInterface();
/// A const constructor.
const RealSenseInterface();
/// Decides which implementation to use depending on platform.
factory RealSenseInterface.forPlatform() => Platform.isLinux ? RealSenseFFI() : RealSenseStub();

/// Initializes the RealSense. Returns whether the initialization was successful.
bool init();
/// Releases the RealSense. Calling [init] again should re-open the device.
void dispose();

/// Starts the RealSense stream and waits for a valid frame.
bool startStream();
/// Stops the stream but keeps the device alive.
void stopStream();

/// The width of the frames.
int get width;
/// The height of the frames.
int get height;
/// The depth scale -- each pixel in the depth frame is an integer multiple of this, in meters.
double get scale;

/// Gets the name and model of the camera.
String getName();
/// Gets the currently available frames. May return [nullptr].
Pointer<NativeFrames> getFrames();
}
2 changes: 2 additions & 0 deletions lib/src/realsense/realsense_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import "package:ffi/ffi.dart";

import "package:video/video.dart";

/// An FFI implementation of [RealSenseInterface] using [librealsense](https://github.com/IntelRealSense/librealsense).
class RealSenseFFI extends RealSenseInterface {
/// The native FFI device.
final device = realsenseLib.RealSense_create();
@override late double scale;
@override int height = 0;
Expand Down
1 change: 1 addition & 0 deletions lib/src/realsense/realsense_stub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "dart:ffi";

import "package:video/video.dart";

/// A stub implementation for platforms or devices without the RealSense SDK.
class RealSenseStub extends RealSenseInterface {
@override
bool init() => true;
Expand Down
2 changes: 2 additions & 0 deletions lib/src/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "package:burt_network/burt_network.dart";

import "collection.dart";

/// The socket to send autonomy data to.
final autonomySocket = SocketInfo(address: InternetAddress("192.168.1.30"), port: 8003);

/// Class for the video program to interact with the dashboard
Expand All @@ -24,6 +25,7 @@ class VideoServer extends RoverServer {
collection.parent.send(data: command, id: command.details.name);
}

/// Sends the depth frame to [autonomySocket].
void sendDepthFrame(VideoData frame) =>
sendMessage(frame, destinationOverride: autonomySocket);

Expand Down

0 comments on commit 202827d

Please sign in to comment.