diff --git a/meson.build b/meson.build index 7db25dc..ac49f00 100644 --- a/meson.build +++ b/meson.build @@ -27,7 +27,7 @@ uca_headers = include_directories('@0@/@1@/uca'.format(get_option('prefix'), 'in plugindir = uca_dep.get_pkgconfig_variable('plugindir') phantom = library('ucaphantom', - sources: ['uca-phantom-communicate.c', 'uca-phantom-camera.c'], + sources: ['ringbuf.c', 'uca-phantom-communicate.c', 'uca-phantom-camera.c'], dependencies: deps, include_directories: headers, install: true, diff --git a/ringbuf.c b/ringbuf.c new file mode 100644 index 0000000..4835ad6 --- /dev/null +++ b/ringbuf.c @@ -0,0 +1,215 @@ +/* + * ringbuf.c - C ring buffer (FIFO) implementation. + * + * Written in 2011 by Drew Hess . + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "ringbuf.h" + +struct ringbuf_t { + gpointer buf, head, tail; + gsize element_size, total_size; + GMutex mutex; +}; + +ringbuf_t ringbuf_new (gsize size, guint capacity) { + ringbuf_t rb = g_new0(struct ringbuf_t, 1); + if (rb) { + /* One byte is used for detecting the full condition. */ + rb->element_size = size; + rb->total_size = (capacity + 1) * size; + rb->buf = g_malloc(rb->total_size); + if (rb->buf) + ringbuf_reset(rb); + else { + g_free(rb); + return 0; + } + g_mutex_init(&rb->mutex); + } + return rb; +} + +gsize ringbuf_buffer_size (const struct ringbuf_t *rb) { + return rb->total_size; +} + +void ringbuf_reset (ringbuf_t rb) { + gpointer *current_buf = NULL; + g_mutex_lock(&rb->mutex); + current_buf = rb->buf; + rb->head = current_buf; + rb->tail = current_buf; + g_mutex_unlock(&rb->mutex); +} + +void ringbuf_free (ringbuf_t *rb) { + g_assert(rb && *rb); + g_mutex_clear(&(*rb)->mutex); + g_free((*rb)->buf); + (*rb)->buf = NULL; + g_free(*rb); + *rb = NULL; +} + +gsize ringbuf_capacity (const struct ringbuf_t *rb) { + return ringbuf_buffer_size(rb) - 1; +} + +/* + * Return a pointer to one-past-the-end of the ring buffer's + * contiguous buffer. You shouldn't normally need to use this function + * unless you're writing a new ringbuf_* function. + */ +static gconstpointer ringbuf_end (const struct ringbuf_t *rb) { + return (guint8 *)(rb->buf) + ringbuf_buffer_size(rb); +} + +gsize ringbuf_bytes_free (struct ringbuf_t *rb) { + gsize retval = 0; + const guint8 *tail = ringbuf_tail(rb); + const guint8 *head = ringbuf_head(rb); + + if (head >= tail) + retval = ringbuf_capacity(rb) - (gsize)(head - tail); + else + retval = (gsize)(tail - head) - 1; + + return retval; +} + +gsize ringbuf_bytes_used (struct ringbuf_t *rb) { + return ringbuf_capacity(rb) - ringbuf_bytes_free(rb); +} + +gboolean ringbuf_is_full (struct ringbuf_t *rb) { + return (ringbuf_bytes_free(rb) == 0); +} + +gboolean ringbuf_is_empty (struct ringbuf_t *rb) { + return (ringbuf_bytes_free (rb) == ringbuf_capacity (rb)); +} + +gconstpointer ringbuf_tail (struct ringbuf_t *rb) { + gpointer retval = NULL; + g_mutex_lock(&rb->mutex); + retval = rb->tail; + g_mutex_unlock(&rb->mutex); + return retval; +} + +gconstpointer ringbuf_head (struct ringbuf_t *rb) { + gpointer retval = NULL; + g_mutex_lock(&rb->mutex); + retval = rb->head; + g_mutex_unlock(&rb->mutex); + return retval; +} + +/* + * Given a ring buffer rb and a pointer to a location within its + * contiguous buffer, return the a pointer to the next logical + * location in the ring buffer. + */ +static gpointer ringbuf_nextp (ringbuf_t rb, gconstpointer p) { + /* + * The assert guarantees the expression (++p - rb->buf) is + * non-negative; therefore, the modulus operation is safe and + * portable. + */ + guint8 *p_uint8 = (gpointer)p; + guint8 *buf_uint8 = rb->buf; + p_uint8 += rb->element_size; + + g_return_val_if_fail ((p_uint8 >= buf_uint8) && (p_uint8 < (guint8 *)ringbuf_end(rb)), NULL); + + return buf_uint8 + ((p_uint8 - buf_uint8) % ringbuf_buffer_size(rb)); +} + +gpointer ringbuf_memcpy_into(ringbuf_t dst, gconstpointer src, gsize count) { + const guint8 *u8src = src; + const guint8 *bufend = ringbuf_end(dst); + guint8 *dsthead = ringbuf_head(dst); + + gboolean overflow = count > ringbuf_bytes_free(dst); + gsize nread = 0, n, diff = 0; + + while (nread != count) { + /* don't copy beyond the end of the buffer */ + if (bufend <= dsthead) + break; + + diff = bufend - dsthead; + n = MIN(diff, count - nread); + memcpy(dsthead, u8src + nread, n); + dsthead += n; + nread += n; + + /* wrap? */ + if (dsthead == bufend) + dsthead = dst->buf; + } + + g_mutex_lock(&dst->mutex); + dst->head = dsthead; + g_mutex_unlock(&dst->mutex); + + if (overflow) { + /* mark the ring buffer as full */ + g_mutex_lock(&dst->mutex); + dst->tail = ringbuf_nextp(dst, dsthead); + g_mutex_unlock(&dst->mutex); + + if (!ringbuf_is_full(dst)) { + return NULL; + } + } + + return dsthead; +} + +gpointer ringbuf_memcpy_from (gpointer dst, ringbuf_t src, gsize count) { + gsize bytes_used = ringbuf_bytes_used(src), n = 0, diff = 0; + if (count > bytes_used) { + return NULL; + } + + guint8 *u8dst = dst; + guint8 *bufend = ringbuf_end(src); + guint8 *tail = ringbuf_tail(src); + + gsize nwritten = 0; + while (nwritten != count) { + if (bufend <= tail) + break; + + diff = bufend - tail; + n = MIN(diff, count - nwritten); + memcpy(u8dst + nwritten, tail, n); + tail += n; + nwritten += n; + + /* wrap ? */ + if (tail == bufend) + tail = src->buf; + } + + g_mutex_lock(&src->mutex); + src->tail = tail; + g_mutex_unlock(&src->mutex); + + if (count + ringbuf_bytes_used(src) != bytes_used) { + return NULL; + } + + return tail; +} \ No newline at end of file diff --git a/ringbuf.h b/ringbuf.h new file mode 100644 index 0000000..2606449 --- /dev/null +++ b/ringbuf.h @@ -0,0 +1,148 @@ +#ifndef INCLUDED_RINGBUF_H +#define INCLUDED_RINGBUF_H + +/* + * ringbuf.h - C ring buffer (FIFO) interface. + * + * Written in 2011 by Drew Hess . + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +/* + * A byte-addressable ring buffer FIFO implementation. + * + * The ring buffer's head pointer points to the starting location + * where data should be written when copying data *into* the buffer + * (e.g., with ringbuf_read). The ring buffer's tail pointer points to + * the starting location where data should be read when copying data + * *from* the buffer (e.g., with ringbuf_write). + */ + +#include + +typedef struct ringbuf_t *ringbuf_t; + +/* + * Create a new ring buffer with the given capacity (usable + * bytes). Note that the actual internal buffer size may be one or + * more bytes larger than the usable capacity, for bookkeeping. + * + * Returns the new ring buffer object, or 0 if there's not enough + * memory to fulfill the request for the given capacity. + */ +ringbuf_t ringbuf_new (gsize size, guint capacity); + +/* + * The size of the internal buffer, in bytes. One or more bytes may be + * unusable in order to distinguish the "buffer full" state from the + * "buffer empty" state. + * + * For the usable capacity of the ring buffer, use the + * ringbuf_capacity function. + */ +gsize ringbuf_buffer_size(const struct ringbuf_t *rb); + +/* + * Deallocate a ring buffer, and, as a side effect, set the pointer to + * 0. + */ +void ringbuf_free(ringbuf_t *rb); + +/* + * Reset a ring buffer to its initial state (empty). + */ +void ringbuf_reset(ringbuf_t rb); + +/* + * The usable capacity of the ring buffer, in bytes. Note that this + * value may be less than the ring buffer's internal buffer size, as + * returned by ringbuf_buffer_size. + */ +gsize ringbuf_capacity(const struct ringbuf_t *rb); + +/* + * The number of free/available bytes in the ring buffer. This value + * is never larger than the ring buffer's usable capacity. + */ +gsize ringbuf_bytes_free(struct ringbuf_t *rb); + +/* + * The number of bytes currently being used in the ring buffer. This + * value is never larger than the ring buffer's usable capacity. + */ +gsize ringbuf_bytes_used(struct ringbuf_t *rb); + +int ringbuf_is_full(struct ringbuf_t *rb); + +int ringbuf_is_empty(struct ringbuf_t *rb); + +/* + * access to the head and tail pointers of the ring buffer. + */ +gconstpointer ringbuf_tail(struct ringbuf_t *rb); + +gconstpointer ringbuf_head(struct ringbuf_t *rb); + +/* + * Copy n bytes from a contiguous memory area src into the ring buffer + * dst. Returns the ring buffer's new head pointer. + * + * It is possible to copy more data from src than is available in the + * buffer; i.e., it's possible to overflow the ring buffer using this + * function. When an overflow occurs, the state of the ring buffer is + * guaranteed to be consistent, including the head and tail pointers; + * old data will simply be overwritten in FIFO fashion, as + * needed. However, note that, if calling the function results in an + * overflow, the value of the ring buffer's tail pointer may be + * different than it was before the function was called. + */ +gpointer ringbuf_memcpy_into(ringbuf_t dst, gconstpointer src, gsize count); + +/* + * Copy n bytes from the ring buffer src, starting from its tail + * pointer, into a contiguous memory area dst. Returns the value of + * src's tail pointer after the copy is finished. + * + * Note that this copy is destructive with respect to the ring buffer: + * the n bytes copied from the ring buffer are no longer available in + * the ring buffer after the copy is complete, and the ring buffer + * will have n more free bytes than it did before the function was + * called. + * + * This function will *not* allow the ring buffer to underflow. If + * count is greater than the number of bytes used in the ring buffer, + * no bytes are copied, and the function will return 0. + */ +gpointer ringbuf_memcpy_from(gpointer dst, ringbuf_t src, gsize count); + +/* + * This convenience function calls write(2) on the file descriptor fd, + * using the ring buffer rb as the source buffer for writing (starting + * at the ring buffer's tail pointer), and returns the value returned + * by write(2). It will only call write(2) once, and may return a + * short count. + * + * Note that this copy is destructive with respect to the ring buffer: + * any bytes written from the ring buffer to the file descriptor are + * no longer available in the ring buffer after the copy is complete, + * and the ring buffer will have N more free bytes than it did before + * the function was called, where N is the value returned by the + * function (unless N is < 0, in which case an error occurred and no + * bytes were written). + * + * This function will *not* allow the ring buffer to underflow. If + * count is greater than the number of bytes used in the ring buffer, + * no bytes are written to the file descriptor, and the function will + * return 0. + */ +gssize ringbuf_write(int fd, ringbuf_t rb, gsize count); + +#endif /* INCLUDED_RINGBUF_H */ diff --git a/test/meson.build b/test/meson.build index e07375d..db52d6e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -12,7 +12,7 @@ test_network = executable( 'test_unit_Variables', - sources: ['test_unit_variables.c', '../uca-phantom-communicate.c'], + sources: ['test_unit_variables.c', '../uca-phantom-communicate.c', '../ringbuf.c'], dependencies: deps, # link_with: phantom, c_args: c_args, diff --git a/uca-phantom-camera.c b/uca-phantom-camera.c index 808150d..28aaf45 100644 --- a/uca-phantom-camera.c +++ b/uca-phantom-camera.c @@ -69,6 +69,7 @@ enum { PROP_XNETCARD, PROP_XENABLED, PROP_LIVE_IMAGES, + PROP_NB_RECORDINGS, N_PHANTOM_PROPERTIES }; @@ -87,12 +88,14 @@ const gfloat temp_min = 5.0, temp_max = 50.0; // Celsius struct _UcaPhantomCameraPrivate { GError *construct_error; GCancellable *accept; + gboolean constructed; // Base class properties + Phantom specific properties gchar *name; gfloat sensor_width, sensor_height; guint max_sensor_resolution_width, max_sensor_resolution_height; guint expdead, xinc, yinc; + guint nb_recordings; // nb cines gboolean has_streaming, has_camram_recording; gboolean live_images; CaptureSettings settings; // Groups all the main writeable properties @@ -102,6 +105,8 @@ struct _UcaPhantomCameraPrivate { gboolean xenabled; gboolean xconnected, connected; + // Hack for tracking the last cine used + gboolean started_recording; UcaPhantomCommunicate *communicator; }; @@ -118,6 +123,13 @@ uca_phantom_camera_start_readout (UcaCamera *camera, GError *internal_error = NULL; UcaPhantomCameraPrivate *priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (camera); + g_object_set (priv->communicator, "buffering", priv->live_images, NULL); + if (priv->settings.timestamp_format == TS_NONE) { + g_object_set (priv->communicator, "timestamping", FALSE, NULL); + } else { + g_object_set (priv->communicator, "timestamping", TRUE, NULL); + } + g_print ("Starting readout\n"); // Launch the readout using the communicator if (!uca_phantom_communicate_start_readout (priv->communicator, &internal_error)) { @@ -153,7 +165,6 @@ uca_phantom_camera_start_recording (UcaCamera *camera, GError **error) { g_return_if_fail (error == NULL || *error == NULL); g_return_if_fail (UCA_IS_PHANTOM_CAMERA (camera)); - static guint prev_cine = 0; g_print ("Starting recording\n"); GError *internal_error = NULL; @@ -173,32 +184,42 @@ uca_phantom_camera_start_recording (UcaCamera *camera, priv->xconnected = TRUE; } - if ((priv->settings.timestamp_format != TS_NONE || !priv->xenabled) && !priv->connected) { + if ((priv->settings.timestamp_format != TS_NONE || !priv->xenabled)) { result = uca_phantom_communicate_connect_datastream (priv->communicator, &internal_error); if (result != TRUE && internal_error != NULL) { g_propagate_error (error, internal_error); return; } - priv->connected = TRUE; } - prev_cine = priv->settings.current_cine; - if (!priv->live_images) { priv->settings.current_cine += 1; + // Arm the camera + result = uca_phantom_communicate_arm (priv->communicator, priv->settings.current_cine, &internal_error); + if (result != TRUE && internal_error != NULL) { + g_propagate_error (error, internal_error); + return; + } + + priv->started_recording = TRUE; + + gdouble time_to_record = priv->settings.nb_pre_trigger_frames / (gdouble)priv->settings.frames_per_second; + g_print ("Time to record : %lf\n", time_to_record); + usleep (time_to_record * 1000000); } + else { + // Arm the camera + result = uca_phantom_communicate_arm (priv->communicator, -1, &internal_error); + if (result != TRUE && internal_error != NULL) { + g_propagate_error (error, internal_error); + return; + } - // Arm the camera - result = uca_phantom_communicate_arm (priv->communicator, priv->settings.current_cine, &internal_error); - if (result != TRUE && internal_error != NULL) { - g_propagate_error (error, internal_error); - return; + priv->started_recording = TRUE; } - gdouble time_to_record = priv->settings.nb_pre_trigger_frames / (gdouble)priv->settings.frames_per_second; - g_print ("Time to record : %lf\n", time_to_record); - usleep (time_to_record * 1000000); + g_print ("Current cine : %d\n", priv->settings.current_cine); } static void @@ -209,9 +230,11 @@ uca_phantom_camera_stop_recording (UcaCamera *camera, g_print ("Stoping recording\n"); - /* - Nothing to do, everything is handled automatically by the class destructor - */ + GError *internal_error = NULL; + + if (!uca_phantom_communicate_disarm (UCA_PHANTOM_CAMERA_GET_PRIVATE (camera)->communicator, &internal_error)) { + return; + } } /** @@ -228,6 +251,23 @@ uca_phantom_camera_trigger (UcaCamera *camera, GError *internal_error = NULL; UcaPhantomCameraPrivate *priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (camera); + // Stupid hack: cant call recording multiple times because libuca forbids it. + // So we need to update the cine in which the data is being recorded + // in the trigger function + if (!priv->live_images && priv->started_recording && priv->settings.current_cine > 1) { + // Arm the camera + gboolean result = uca_phantom_communicate_arm (priv->communicator, priv->settings.current_cine, &internal_error); + if (result != TRUE && internal_error != NULL) { + g_propagate_error (error, internal_error); + return; + } + gdouble time_to_record = priv->settings.nb_pre_trigger_frames / (gdouble)priv->settings.frames_per_second; + g_print ("Time to record : %lf\n", time_to_record); + usleep (time_to_record * 1000000); + } + + g_print ("Current cine : %d\n", priv->settings.current_cine); + gboolean res = uca_phantom_communicate_set_settings (priv->communicator, &priv->settings, &internal_error); if (res != TRUE && internal_error != NULL) { g_propagate_error (error, internal_error); @@ -246,7 +286,7 @@ uca_phantom_camera_trigger (UcaCamera *camera, // and wait an extra 100ms to be sure :D gdouble time_to_record = priv->settings.nb_post_trigger_frames / (gdouble)priv->settings.frames_per_second; g_print ("Time to record : %lf\n", time_to_record); - usleep (time_to_record * 1000000 + 100000); + g_usleep (time_to_record * G_USEC_PER_SEC + 100000); g_print ("img_format : %s\n", ImageFormatSpecs[priv->settings.image_format].format_string); @@ -261,6 +301,8 @@ uca_phantom_camera_trigger (UcaCamera *camera, g_print ("Error : %s\n", internal_error->message); return; } + + priv->settings.current_cine += 1; } static void @@ -291,20 +333,20 @@ uca_phantom_camera_grab (UcaCamera *camera, GError *internal_error = NULL; if (priv->live_images) { - if (!uca_phantom_communicate_request_images ( - priv->communicator, - &(priv->settings), - &internal_error)) { + if (!uca_phantom_communicate_grab_buffered_image (priv->communicator, data, &internal_error)) { + g_propagate_error (error, internal_error); + return FALSE; + } + } + else { + if (!uca_phantom_communicate_grab_image (priv->communicator, data, &internal_error)) { g_propagate_error (error, internal_error); return FALSE; } } // Grab single image! - if (!uca_phantom_communicate_grab_image (priv->communicator, data, &internal_error)) { - g_propagate_error (error, internal_error); - return FALSE; - } + return TRUE; } @@ -318,7 +360,6 @@ uca_phantom_camera_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { UcaPhantomCameraPrivate *priv; - g_print ("Setting property %d\n", property_id); priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (object); UcaPhantomCommunicate *communicator = priv->communicator; @@ -354,10 +395,9 @@ uca_phantom_camera_set_property (GObject *object, priv->settings.trigger_type = g_value_get_uint (value); break; case PROP_EXPOSURE_TIME: - priv->settings.exposure_time = g_value_get_uint (value); + priv->settings.exposure_time = g_value_get_double (value); break; case PROP_FRAMES_PER_SECOND: - g_print ("Setting FPS to %f\n", g_value_get_double (value)); priv->settings.frames_per_second = g_value_get_double (value); break; case PROP_ROI_X: @@ -404,11 +444,9 @@ uca_phantom_camera_set_property (GObject *object, priv->settings.aexpcomp = g_value_get_float (value); break; case PROP_NB_POST_TRIGGER_FRAMES: - g_print ("Setting nb_post_trigger_frames to %d\n", g_value_get_uint (value)); priv->settings.nb_post_trigger_frames = g_value_get_uint (value); break; case PROP_NB_PRE_TRIGGER_FRAMES: - g_print ("Setting nb_pre_trigger_frames to %d\n", g_value_get_uint (value)); priv->settings.nb_pre_trigger_frames = g_value_get_uint (value); break; case PROP_CURRENT_CINE: @@ -427,21 +465,19 @@ uca_phantom_camera_set_property (GObject *object, priv->settings.timestamp_format = g_value_get_uint (value); break; case PROP_XNETCARD: - g_print ("Setting xnetcard to %s\n", g_value_get_string (value)); g_free (priv->xnetcard); priv->xnetcard = g_strdup (g_value_get_string (value)); - g_object_set (priv->communicator, "xnetcard", priv->xnetcard, NULL); break; case PROP_XENABLED: priv->xenabled = g_value_get_boolean (value); - g_object_set (priv->communicator, "xenabled", priv->xenabled, NULL); break; case PROP_LIVE_IMAGES: priv->live_images = g_value_get_boolean (value); if (priv->live_images) priv->settings.current_cine = -1; - else - priv->settings.current_cine = 1; + break; + case PROP_NB_RECORDINGS: + priv->nb_recordings = g_value_get_uint (value); break; default: // Warn if the property is not defined in this class @@ -450,11 +486,15 @@ uca_phantom_camera_set_property (GObject *object, } // Send the settings to the phantom - gboolean res = uca_phantom_communicate_set_settings (priv->communicator, &(priv->settings), &internal_error); - if (!res || internal_error != NULL) { - g_warning ("Failed to set capture settings: %s", internal_error->message); - g_error_free (internal_error); + if (priv->constructed) { + if (!uca_phantom_communicate_set_nb_cines (priv->communicator, priv->nb_recordings, &internal_error)) { + g_message ("Error setting nb_cines: %s", internal_error->message); + g_error_free (internal_error); + } + } + else { } + } /** @@ -466,7 +506,6 @@ uca_phantom_camera_get_property (GObject *object, GValue *value, GParamSpec *pspec) { UcaPhantomCameraPrivate *priv; - g_print ("Getting property %d\n", property_id); priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (object); @@ -500,12 +539,10 @@ uca_phantom_camera_get_property (GObject *object, g_value_set_uint (value, priv->settings.trigger_type); break; case PROP_EXPOSURE_TIME: - g_value_set_uint (value, priv->settings.exposure_time); + g_value_set_double (value, priv->settings.exposure_time); break; case PROP_FRAMES_PER_SECOND: - g_print ("Getting FPS to %f\n", priv->settings.frames_per_second); g_value_set_double (value, priv->settings.frames_per_second); - break; case PROP_ROI_X: g_value_set_int (value, priv->settings.roi_pixel_x); @@ -580,6 +617,9 @@ uca_phantom_camera_get_property (GObject *object, case PROP_LIVE_IMAGES: g_value_set_uint (value, priv->live_images); break; + case PROP_NB_RECORDINGS: + g_value_set_uint (value, priv->nb_recordings); + break; default: // Warn if the property is not defined in this class G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -590,19 +630,23 @@ static void uca_phantom_camera_dispose (GObject *object) { UcaPhantomCameraPrivate *priv; - g_print ("Disposing phantom camera\n") ; - - // g_free (priv->name); - priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (object); G_OBJECT_CLASS (uca_phantom_camera_parent_class)->dispose (object); + + if (priv->xnetcard) { + g_free (priv->xnetcard); + priv->xnetcard = NULL; + } + if (priv->name) { + g_free (priv->name); + priv->name = NULL; + } } static void uca_phantom_camera_finalize (GObject *object) { UcaPhantomCameraPrivate *priv; - g_print ("Finalizing phantom camera\n") ; priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (object); @@ -612,7 +656,21 @@ uca_phantom_camera_finalize (GObject *object) { if (priv->communicator) { g_object_unref (priv->communicator); priv->communicator = NULL; - } + } +} + +static void +uca_phantom_camera_constructed (GObject *object) { + UcaPhantomCamera *camera; + UcaPhantomCameraPrivate *priv; + GError *error = NULL; + + camera = UCA_PHANTOM_CAMERA (object); + priv = camera->priv; + + priv->constructed = TRUE; + + G_OBJECT_CLASS (uca_phantom_camera_parent_class)->constructed (object); } static gboolean @@ -625,8 +683,6 @@ ufo_net_camera_initable_init (GInitable *initable, g_return_val_if_fail (UCA_IS_PHANTOM_CAMERA (initable), FALSE); - g_print ("Initializing phantom camera (INITABLE)\n") ; - if (cancellable != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cancellable initialization not supported"); @@ -646,11 +702,10 @@ ufo_net_camera_initable_init (GInitable *initable, // Create a phantom communicator object priv->communicator = uca_phantom_communicate_new (); - g_print ("Setting communicator properties\n") ; - g_object_set (priv->communicator, "xnetcard", priv->xnetcard, "xenabled", priv->xenabled, + "timestamping", TRUE, NULL); // Connect the control streamm to the camera @@ -662,7 +717,6 @@ ufo_net_camera_initable_init (GInitable *initable, priv->connected = TRUE; } - GValue value = G_VALUE_INIT; if (!uca_phantom_communicate_get_variable (priv->communicator, UNIT_INFO_XMAX, &value, &internal_error)) { g_propagate_error (error, internal_error); @@ -706,36 +760,11 @@ ufo_net_camera_initable_init (GInitable *initable, priv->sensor_height = sensor_height; priv->has_streaming = FALSE; priv->has_camram_recording = FALSE; - priv->settings = (CaptureSettings){ - .sync_mode = SYNC_MODE_VIDEO_FRAME_RATE, - .acquisition_mode = ACQUISITION_MODE_HS_BINNED, - .image_format = IMG_P12L, - .timestamp_format = TS_NONE, - .sensor_pixel_width = sensor_pixel_width, - .sensor_pixel_height = sensor_pixel_height, - .sensor_bit_depth = ImageFormatSpecs[IMG_P12L].bit_depth, - .trigger_source = UCA_CAMERA_TRIGGER_SOURCE_SOFTWARE, - .trigger_type = UCA_CAMERA_TRIGGER_TYPE_EDGE, - .frames_per_second = 100.0, - .exposure_time = 459, - .roi_pixel_x = 0, - .roi_pixel_y = 0, - .roi_pixel_width = priv->max_sensor_resolution_width, - .roi_pixel_height = priv->max_sensor_resolution_height, - .roi_width_multiplier = priv->xinc , - .roi_height_multiplier = priv->yinc , - .focal_length = 0.0, - .aperture = 0.0, - .edr_exp = 459, - .shutter_off = 1, - .aexpmode = AUTO_EXP_MODE_AVERAGE, - .aexpcomp = 0.0, - .nb_post_trigger_frames = 0, - .nb_pre_trigger_frames = 0, - .current_cine = 1, - }; - g_print ("Phantom camera initialized\n") ; + priv->settings.sensor_pixel_width = sensor_pixel_width; + priv->settings.sensor_pixel_height = sensor_pixel_height; + priv->settings.roi_pixel_width = priv->max_sensor_resolution_width; + priv->settings.roi_pixel_height = priv->max_sensor_resolution_height; return TRUE; } @@ -752,11 +781,9 @@ static void uca_phantom_camera_class_init (UcaPhantomCameraClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); UcaCameraClass *camera_class = UCA_CAMERA_CLASS (klass); - - g_print ("Initializing phantom camera class\n") ; - oclass->set_property = uca_phantom_camera_set_property; oclass->get_property = uca_phantom_camera_get_property; + oclass->constructed = uca_phantom_camera_constructed; oclass->dispose = uca_phantom_camera_dispose; oclass->finalize = uca_phantom_camera_finalize; @@ -771,29 +798,29 @@ uca_phantom_camera_class_init (UcaPhantomCameraClass *klass) { // Add the phantom specific unit variables as properties uca_phantom_camera_properties[PROP_FOCAL_LENGTH] = - g_param_spec_double ("focal-length", + g_param_spec_float ("focal-length", "Focal length", "Focal length", - 0, G_MAXDOUBLE, 0, + 0, G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_APERTURE] = - g_param_spec_double ("aperture", + g_param_spec_float ("aperture", "Aperture", "Aperture", - 0, G_MAXDOUBLE, 0, + 0, G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_EDR_EXP] = - g_param_spec_double ("edrexp", + g_param_spec_uint ("edrexp", "EDR exposure time", "EDR exposure time", - 0, G_MAXDOUBLE, 0, + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_SHUTTER_OFF] = - g_param_spec_boolean ("shutteroff", + g_param_spec_uint ("shutteroff", "Shutter off", "Shutter off", - FALSE, + 0, 1, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_AEXPMODE] = g_param_spec_uint ("aexpmode", @@ -803,10 +830,10 @@ uca_phantom_camera_class_init (UcaPhantomCameraClass *klass) { G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_AEXPCOMP] = - g_param_spec_double ("aexpcomp", + g_param_spec_float ("aexpcomp", "Auto exposure compensation", "Auto exposure compensation", - 0, G_MAXDOUBLE, 0, + 0, G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_NB_POST_TRIGGER_FRAMES] = g_param_spec_uint ("postframes", @@ -824,7 +851,7 @@ uca_phantom_camera_class_init (UcaPhantomCameraClass *klass) { g_param_spec_uint ("cine", "Current cine number", "Current cine number", - 0, G_MAXUINT, 1, + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_SYNC_MODE] = g_param_spec_uint ("syncmode", @@ -858,7 +885,7 @@ uca_phantom_camera_class_init (UcaPhantomCameraClass *klass) { g_param_spec_string ("xnetcard", "10 Gb NIC", "10 Gb NIC", - NULL, + "ens21f0", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); uca_phantom_camera_properties[PROP_XENABLED] = g_param_spec_boolean ("xenabled", @@ -873,6 +900,12 @@ uca_phantom_camera_class_init (UcaPhantomCameraClass *klass) { "rab images from live cine", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); + uca_phantom_camera_properties[PROP_NB_RECORDINGS] = + g_param_spec_uint ("nb-recordings", + "Number of recordings", + "Number of recordings", + 0, G_MAXUINT, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); // Implement the base class properties for (guint i = 0; base_overrideables[i] != 0; i++) { @@ -894,8 +927,31 @@ uca_phantom_camera_init (UcaPhantomCamera *self) { UcaPhantomCameraPrivate *priv; self->priv = priv = UCA_PHANTOM_CAMERA_GET_PRIVATE (self); - g_print ("Initializing phantom camera instance\n") ; - + priv->settings = (CaptureSettings){ + .sync_mode = SYNC_MODE_VIDEO_FRAME_RATE, + .acquisition_mode = ACQUISITION_MODE_HS_BINNED, + .image_format = IMG_P12L, + .timestamp_format = TS_NONE, + .sensor_bit_depth = ImageFormatSpecs[IMG_P12L].bit_depth, + .trigger_source = UCA_CAMERA_TRIGGER_SOURCE_SOFTWARE, + .trigger_type = UCA_CAMERA_TRIGGER_TYPE_EDGE, + .frames_per_second = 100.0, + .exposure_time = 459, + .roi_pixel_x = 0, + .roi_pixel_y = 0, + .roi_width_multiplier = priv->xinc , + .roi_height_multiplier = priv->yinc , + .focal_length = 0.0, + .aperture = 0.0, + .edr_exp = 459, + .shutter_off = 1, + .aexpmode = AUTO_EXP_MODE_AVERAGE, + .aexpcomp = 0.0, + .nb_post_trigger_frames = 0, + .nb_pre_trigger_frames = 0, + .current_cine = 0, + }; + priv->started_recording = FALSE; } G_MODULE_EXPORT GType @@ -904,7 +960,6 @@ camera_plugin_get_type (void) { } UcaPhantomCamera *uca_phantom_camera_new (void) { - g_print ("Creating phantom camera instance\n") ; return UCA_PHANTOM_CAMERA ( g_object_new ( UCA_TYPE_PHANTOM_CAMERA, NULL)); diff --git a/uca-phantom-commands.h b/uca-phantom-commands.h index f249d76..3e58b56 100644 --- a/uca-phantom-commands.h +++ b/uca-phantom-commands.h @@ -1,3 +1,6 @@ +#ifndef UCA_PHANTOM_COMMANDS_H +#define UCA_PHANTOM_COMMANDS_H + #include #include #include @@ -368,4 +371,6 @@ PhantomCommand Commands[] = { {"preset", 4, CMD_USE_COLOR_PRESET}, // 4 optional {"mmset", 3, CMD_SET_MULTI_MATRIX_AXIS}, {NULL,} -}; \ No newline at end of file +}; + +#endif // COMMANDS_H \ No newline at end of file diff --git a/uca-phantom-communicate.c b/uca-phantom-communicate.c index 641e887..d56b525 100644 --- a/uca-phantom-communicate.c +++ b/uca-phantom-communicate.c @@ -1,38 +1,51 @@ +/** + * @file uca-phantom-communicate.c + * @brief Implementation of the UcaPhantomCommunicate class + * @ingroup UcaPhantomCommunicate + * + * @author Gabriel Lefloch + */ + // GLib and GObject related includes #include -#include #include - -#include +#include #include #include #include +#include // Intel intrinsics #include // Network related includes -#include -#include -#include #include #include +#include +#include +#include #include "uca-phantom-communicate.h" #include "uca-phantom-variables.h" #include "uca-phantom-commands.h" +#include "ringbuf.h" -#define ETHERNET_HEADER_SIZE 32 // 16 bytes for L1 ethernet header, 16 bytes for custom header +// Note: if you wish to mess everything up, please change the following macros +#define ETHERNET_HEADER_SIZE 32 // 16 bytes for L1 ethernet header, 16 bytes for custom header #define MAX_KERNEL_BUF_SIZE 2147483647 // 2GBytes = 2^31 - 1 Bytes #define MAX_HEAP_BUF_SIZE MAX_KERNEL_BUF_SIZE -#define MAX_NUMBER_CINE_DATA 1000 // Maximum number of cine data packets to receive -#define MAX_SENSOR_PIXEL_WIDTH 2048 // Maximum sensor pixel width +#define MAX_SENSOR_PIXEL_WIDTH 2048 // Maximum sensor pixel width #define MAX_SENSOR_PIXEL_HEIGHT 1952 // Maximum sensor pixel height +#define MAX_NB_IMAGES_BUFFERING 50 /** * TODO: - * - Limit nb_images in request_images to frcount unit variable + * - Verify the requested data size is smaller than the available space in the + * camera cine + * - timestamps + * - signals ? + * - Checkout the memread nonsense, external pins and whatnot * - Implement cancelable functions * - Add documentation */ @@ -48,6 +61,7 @@ enum { PROP_CONTROL_PORT, PROP_PHANTOM_IPSOURCE, PROP_TIMESTAMPING, + PROP_BUFFERING, N_PROPERTIES } UcaPhantomCommunicateProperties; @@ -81,42 +95,43 @@ typedef enum { * @{ */ -typedef struct { +typedef struct +{ gint x, y; guint w, h, threshold, area, speed, mode; } Trigger; -typedef struct _short_time_stamp { // cam.tsformat = 0 - unsigned int csecs; // time from beginning of the year in 1/100 sec units +typedef struct _short_time_stamp { // cam.tsformat = 0 + unsigned int csecs; // time from beginning of the year in 1/100 sec units unsigned short exptime; // exposure time in us - unsigned short frac; // bits[15..2]: fractions (us to 10000); b[1]:event;[0]:lock + unsigned short frac; // bits[15..2]: fractions (us to 10000); b[1]:event;[0]:lock } short_time_stamp; -typedef struct _short_time_stamp32 { // cam.tsformat = 1 - unsigned int csecs; // time from beginning of the year in 1/100 sec units - unsigned short exptime; // exposure time in us - unsigned short frac; // bits[15..2]: fractions (us to 10000); b[1]:event; b[0]:lock +typedef struct _short_time_stamp32 { // cam.tsformat = 1 + unsigned int csecs; // time from beginning of the year in 1/100 sec units + unsigned short exptime; // exposure time in us + unsigned short frac; // bits[15..2]: fractions (us to 10000); b[1]:event; b[0]:lock unsigned short exptime32; // exposure time extension (1/65536 of a us) - unsigned short frac32; // time stamp extension (1/65536 of a us) + unsigned short frac32; // time stamp extension (1/65536 of a us) } short_time_stamp32; -typedef struct _long_time_stamp { // cam.tsformat = 2 - unsigned int csecs; // time from beginning of the year in 1/100 sec units +typedef struct _long_time_stamp { // cam.tsformat = 2 + unsigned int csecs; // time from beginning of the year in 1/100 sec units unsigned short exptime; // exposure time in us - unsigned short frac; // bits[15..2]: fractions (us to 10000); bit[1]:event; bit[0]:lock - unsigned int range_d0; // first 32bits received as rangedata, lsb first, big endian - unsigned int range_d1; // second 32bits received as rangedata, lsb first, big endian - unsigned int range_d2; // third 32bits received as rangedata, lsb first, big endian - unsigned int range_d3; // fourth 32bits received as rangedata, lsb first, big endian + unsigned short frac; // bits[15..2]: fractions (us to 10000); bit[1]:event; bit[0]:lock + unsigned int range_d0; // first 32bits received as rangedata, lsb first, big endian + unsigned int range_d1; // second 32bits received as rangedata, lsb first, big endian + unsigned int range_d2; // third 32bits received as rangedata, lsb first, big endian + unsigned int range_d3; // fourth 32bits received as rangedata, lsb first, big endian } long_time_stamp; -typedef struct _long_time_stamp32 { // cam.tsformat = 3 - unsigned int csecs; // time from beginning of the year in 1/100 sec units - unsigned short exptime; // exposure time in us - unsigned short frac; // bits[15..2]: fractions (us to 10000); bit[1]:event; bit[0]:lock +typedef struct _long_time_stamp32 { // cam.tsformat = 3 + unsigned int csecs; // time from beginning of the year in 1/100 sec units + unsigned short exptime; // exposure time in us + unsigned short frac; // bits[15..2]: fractions (us to 10000); bit[1]:event; bit[0]:lock unsigned short exptime32; // exposure time extension (1/65536 of a us) - unsigned short frac32; // time stamp extension (1/65536 of a us) - unsigned int range_d0; // first 32bits received as rangedata, lsb first, big endian - unsigned int range_d1; // second 32bits received as rangedata, lsb first, big endian - unsigned int range_d2; // third 32bits received as rangedata, lsb first, big endian - unsigned int range_d3; // fourth 32bits received as rangedata, lsb first, big endian + unsigned short frac32; // time stamp extension (1/65536 of a us) + unsigned int range_d0; // first 32bits received as rangedata, lsb first, big endian + unsigned int range_d1; // second 32bits received as rangedata, lsb first, big endian + unsigned int range_d2; // third 32bits received as rangedata, lsb first, big endian + unsigned int range_d3; // fourth 32bits received as rangedata, lsb first, big endian } long_time_stamp32; /** @} */ @@ -128,23 +143,32 @@ typedef struct _long_time_stamp32 { // cam.tsformat */ struct _PhantomRequest { PhantomCommand command; - gchar *message; + gchar* message; gsize size; gssize write_size; }; struct _PhantomReply { - gchar *raw; + gchar* raw; GValue value; gsize size; gssize read_size; }; -typedef struct _InternalRequest { +typedef struct _ImageRequest { + gboolean end_request; + gint start; guint64 nb_images; ImageFormat img_format; + guint buffer_index; +} ImageRequest; + +typedef struct _TsRequest { gboolean end_request; -} InternalRequest; + gint start; + guint count; + TimestampFormat TsFormat; +} TsRequest; /** * @brief Timestamp union @@ -158,7 +182,7 @@ typedef union { short_time_stamp32 ts1; long_time_stamp ts2; long_time_stamp32 ts3; -} TimeStamp; +} Timestamp; /** @} */ /** @@ -172,7 +196,7 @@ typedef union { * */ typedef struct _CineData { - CaptureSettings *Settings; + CaptureSettings* Settings; ImageFormat ImgFormat; TimestampFormat TsFormat; @@ -181,67 +205,65 @@ typedef struct _CineData { gsize SizePerImageRaw; gsize SizePerImageUnpacked; - guint8 *RawImages; + guint8* RawImages; gpointer UnpackedImages; - gpointer RawTimestamps; + guint buffer_index; } CineData; +typedef struct _TimestampData { + gint start; + guint count; + TimestampFormat TsFormat; + Timestamp* timestamps; + gboolean end_request; +} TimestampData; + static GEnumValue sync_mode_values[] = { - {SYNC_MODE_FREE_RUN, "SYNC_MODE_FREE_RUN", "sync_mode_free_run"}, - {SYNC_MODE_FSYNC, "SYNC_MODE_FSYNC", "sync_mode_fsync"}, - {SYNC_MODE_IRIG, "SYNC_MODE_IRIG", "sync_mode_irig"}, - {SYNC_MODE_VIDEO_FRAME_RATE, "SYNC_MODE_VIDEO_FRAME_RATE", "sync_mode_video_frame_rate"}, - {0, NULL, NULL}}; + { SYNC_MODE_FREE_RUN, "SYNC_MODE_FREE_RUN", "sync_mode_free_run" }, + { SYNC_MODE_FSYNC, "SYNC_MODE_FSYNC", "sync_mode_fsync" }, + { SYNC_MODE_IRIG, "SYNC_MODE_IRIG", "sync_mode_irig" }, + { SYNC_MODE_VIDEO_FRAME_RATE, "SYNC_MODE_VIDEO_FRAME_RATE", "sync_mode_video_frame_rate" }, + { 0, NULL, NULL } +}; static GEnumValue acquisition_mode_values[] = { - {ACQUISITION_MODE_STANDARD, "ACQUISITION_MODE_STANDARD", "acquisition_mode_standard"}, - {ACQUISITION_MODE_STANDARD_BINNED, "ACQUISITION_MODE_STANDARD_BINNED", "acquisition_mode_standard_binned"}, - {ACQUISITION_MODE_HS, "ACQUISITION_MODE_HS", "acquisition_mode_hs"}, - {ACQUISITION_MODE_HS_BINNED, "ACQUISITION_MODE_HS_BINNED", "acquisition_mode_hs_binned"}, - {0, NULL, NULL}}; - -const ImageFormatSpec ImageFormatSpecs[] = { - {"8", 8, 1}, - {"8R", 8, 1}, - {"P16", 16, 2}, - {"P16R", 16, 2}, - {"P10", 10, 1.25}, - {"P12L", 12, 1.5}}; - -const TimestampSpec TimestampSpecs[] = { - {"0", sizeof(short_time_stamp), 0}, - {"1", sizeof(short_time_stamp32), 0}, - {"2", sizeof(long_time_stamp), 0}, - {"3", sizeof(long_time_stamp32), 0}}; + { ACQUISITION_MODE_STANDARD, "ACQUISITION_MODE_STANDARD", "acquisition_mode_standard" }, + { ACQUISITION_MODE_STANDARD_BINNED, "ACQUISITION_MODE_STANDARD_BINNED", "acquisition_mode_standard_binned" }, + { ACQUISITION_MODE_HS, "ACQUISITION_MODE_HS", "acquisition_mode_hs" }, + { ACQUISITION_MODE_HS_BINNED, "ACQUISITION_MODE_HS_BINNED", "acquisition_mode_hs_binned" }, + { 0, NULL, NULL } +}; -const gdouble MaxNumberImages = MAX_KERNEL_BUF_SIZE / ((double)(MAX_SENSOR_PIXEL_HEIGHT * MAX_SENSOR_PIXEL_WIDTH) + ETHERNET_HEADER_SIZE); +const ImageFormatSpec ImageFormatSpecs[] = { { "8", 8, 1 }, { "8R", 8, 1 }, { "P16", 16, 2 }, { "P16R", 16, 2 }, { "P10", 10, 1.25 }, { "P12L", 12, 1.5 } }; -const guint MaxNumberImagesPerRequest[] = { - (MaxNumberImages), - (MaxNumberImages), - (MaxNumberImages / 2), - (MaxNumberImages / 2), - (MaxNumberImages / 1.25), - (MaxNumberImages / 1.5)}; +const TimestampSpec TimestampSpecs[] = { { "0", sizeof(short_time_stamp) }, + { "1", sizeof(short_time_stamp32) }, + { "2", sizeof(long_time_stamp) }, + { "3", sizeof(long_time_stamp32) } }; -// Forward declaration of overrideable functions -static void uca_phantom_communicate_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); -static void uca_phantom_communicate_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); -static void uca_phantom_communicate_constructed(GObject *object); -static void uca_phantom_communicate_dispose(GObject *object); -static void uca_phantom_communicate_finalize(GObject *object); +const gdouble MaxNumberImages = MAX_KERNEL_BUF_SIZE / ((double)(MAX_SENSOR_PIXEL_HEIGHT * MAX_SENSOR_PIXEL_WIDTH) + ETHERNET_HEADER_SIZE); -static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate *self, guint16 *width, guint16 *height, GError **error_loc); -gboolean uca_phantom_communicate_get_settings(UcaPhantomCommunicate *self, CaptureSettings *settings, GError **error_loc); +const guint MaxNumberImagesPerRequest[] = { (MaxNumberImages), (MaxNumberImages), (MaxNumberImages / 2), + (MaxNumberImages / 2), (MaxNumberImages / 1.25), (MaxNumberImages / 1.5) }; -static GParamSpec *uca_phantom_communicate_properties[N_PROPERTIES] = { +// Forward declaration of overrideable functions +static void uca_phantom_communicate_set_property(GObject* object, guint property_id, const GValue* value, + GParamSpec* pspec); +static void uca_phantom_communicate_get_property(GObject* object, guint property_id, GValue* value, GParamSpec* pspec); +static void uca_phantom_communicate_constructed(GObject* object); +static void uca_phantom_communicate_dispose(GObject* object); +static void uca_phantom_communicate_finalize(GObject* object); + +static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate* self, guint16* width, guint16* height, + GError** error_loc); +static GParamSpec* uca_phantom_communicate_properties[N_PROPERTIES] = { NULL, }; struct _UcaPhantomCommunicate { GObject parent_object; - gboolean xenabled, timestamping; + gboolean xenabled, timestamping, buffering; gchar *phantom_ip, *phantom_xip; gchar *netcard_ip, *netcard_xip; gchar *netcard, *xnetcard; @@ -256,40 +278,50 @@ struct _UcaPhantomCommunicate { ConnectionState xdata_connection_state; AcquisitionState local_acquisition_state; AcquisitionState phantom_acquisition_state; + AcquisitionState buffer_state; // Currently buffering images or not ? // camera setup variables CaptureSettings settings; // Command stream connection variables - GSocketConnection *control_connection; - GSocketClient *control_client; - GOutputStream *output_controlstream; - GInputStream *input_controlstream; + GSocketConnection* control_connection; + GSocketClient* control_client; + GOutputStream* output_controlstream; + GInputStream* input_controlstream; // Data stream connection variables (1 GbE) - GSocketService *service; - GSocketConnection *data_connection; - GInputStream *input_datastream; - GOutputStream *output_datastream; + GSocketService* service; + GSocketConnection* data_connection; + GInputStream* input_datastream; + GOutputStream* output_datastream; // Data stream connection variables (10 GbE) - pcap_t *handle; - GThread *data_receiver; - GThread *data_unpacker; - GPtrArray *unpacked_images; - GPtrArray *capture_settings; - GAsyncQueue *packed_queue; - GAsyncQueue *unpacked_queue; - GAsyncQueue *time_queue; - GAsyncQueue *request_queue; + pcap_t* handle; + GThread* data_receiver; + GThread* data_unpacker; + GThread* ts_receiver; + GPtrArray* unpacked_images; + GPtrArray* capture_settings; + guint buffer_index_counter; // used to keep track of the buffer index of the + // unpacked_images + GAsyncQueue* packed_queue; + GAsyncQueue* unpacked_queue; + GAsyncQueue* ts_queue; + GAsyncQueue* ts_request_queue; + GAsyncQueue* request_queue; + + // Live buffering + ringbuf_t* ring_buffer; + GThread* buffering_thread; }; G_DEFINE_TYPE(UcaPhantomCommunicate, uca_phantom_communicate, G_TYPE_OBJECT) G_DEFINE_QUARK("uca-phantom-communicate-error-quark", uca_phantom_communicate_error) -static void uca_phantom_communicate_class_init(UcaPhantomCommunicateClass *class) { - GObjectClass *gobject_class = G_OBJECT_CLASS(class); +static void uca_phantom_communicate_class_init(UcaPhantomCommunicateClass* class) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS(class); gobject_class->set_property = uca_phantom_communicate_set_property; gobject_class->get_property = uca_phantom_communicate_get_property; @@ -298,89 +330,43 @@ static void uca_phantom_communicate_class_init(UcaPhantomCommunicateClass *class gobject_class->finalize = uca_phantom_communicate_finalize; // install properties - uca_phantom_communicate_properties[PROP_PHANTOM_IP] = - g_param_spec_string( - "phantom_ip", - "Phantom IP address", - "IP address of the Phantom camera over normal 1Gb ethernet", - "100.100.189.164", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - uca_phantom_communicate_properties[PROP_PHANTOM_XIP] = - g_param_spec_string( - "phantom_xip", - "Phantom 10Gb IP address", - "IP address of the Phantom camera over a 10Gb ethernet", - "172.16.31.157", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - uca_phantom_communicate_properties[PROP_NETCARD_IP] = - g_param_spec_string( - "netcard_ip", - "Network card IP", - "IP address of the network card used for 1Gb ethernet", - "100.100.100.1", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - uca_phantom_communicate_properties[PROP_NETCARD_XIP] = - g_param_spec_string( - "netcard_xip", - "10Gb network card IP", - "IP address of the network card used for 10Gb ethernet", - "172.16.0.1", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - uca_phantom_communicate_properties[PROP_NETCARD] = - g_param_spec_string( - "netcard", - "Network card", - "Name of the network card used for 1Gb ethernet", - "eth0", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - uca_phantom_communicate_properties[PROP_XNETCARD] = - g_param_spec_string( - "xnetcard", - "10Gb network card", - "Name of the network card used for 10Gb ethernet", - "eth1", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - uca_phantom_communicate_properties[PROP_XENABLED] = - g_param_spec_boolean( - "xenabled", - "Enable 10Gb data transfer", - "Enable 10Gb data transfer", - TRUE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - uca_phantom_communicate_properties[PROP_CONTROL_PORT] = - g_param_spec_uint( - "control_port", - "Set connection port", - "Set the port used to establish TCP connection with phantom", - 1024, 49151, 7115, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - uca_phantom_communicate_properties[PROP_PHANTOM_IPSOURCE] = - g_param_spec_uint( - "phantom_ipsource", - "Set the IP source using IP flags", - "Possible flags: USE_ENV, USE_CLASS, USE_DISCOVER.", - 0, N_IP_FLAGS, USE_CLASS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - uca_phantom_communicate_properties[PROP_TIMESTAMPING] = - g_param_spec_boolean( - "timestamping", - "Enable timestamping", - "Enable timestamping", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - g_object_class_install_properties( - gobject_class, - N_PROPERTIES, - uca_phantom_communicate_properties); + uca_phantom_communicate_properties[PROP_PHANTOM_IP] = g_param_spec_string( + "phantom_ip", "Phantom IP address", "IP address of the Phantom camera over normal 1Gb ethernet", + "100.100.189.164", G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + uca_phantom_communicate_properties[PROP_PHANTOM_XIP] = g_param_spec_string( + "phantom_xip", "Phantom 10Gb IP address", "IP address of the Phantom camera over a 10Gb ethernet", + "172.16.31.157", G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + uca_phantom_communicate_properties[PROP_NETCARD_IP] = g_param_spec_string("netcard_ip", "Network card IP", "IP address of the network card used for 1Gb ethernet", + "100.100.100.1", G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + uca_phantom_communicate_properties[PROP_NETCARD_XIP] = g_param_spec_string( + "netcard_xip", "10Gb network card IP", "IP address of the network card used for 10Gb ethernet", "172.16.0.1", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + uca_phantom_communicate_properties[PROP_NETCARD] = g_param_spec_string("netcard", "Network card", "Name of the network card used for 1Gb ethernet", "eth0", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + uca_phantom_communicate_properties[PROP_XNETCARD] = g_param_spec_string("xnetcard", "10Gb network card", "Name of the network card used for 10Gb ethernet", "eth1", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + uca_phantom_communicate_properties[PROP_XENABLED] = g_param_spec_boolean("xenabled", "Enable 10Gb data transfer", "Enable 10Gb data transfer", TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + uca_phantom_communicate_properties[PROP_CONTROL_PORT] = g_param_spec_uint( + "control_port", "Set connection port", "Set the port used to establish TCP connection with phantom", 1024, + 49151, 7115, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + uca_phantom_communicate_properties[PROP_PHANTOM_IPSOURCE] = g_param_spec_uint( + "phantom_ipsource", "Set the IP source using IP flags", "Possible flags: USE_ENV, USE_CLASS, USE_DISCOVER.", 0, + N_IP_FLAGS, USE_CLASS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + uca_phantom_communicate_properties[PROP_TIMESTAMPING] = g_param_spec_boolean( + "timestamping", "Enable timestamping", "Enable timestamping", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + uca_phantom_communicate_properties[PROP_BUFFERING] = g_param_spec_boolean( + "buffering", "Enable buffering", "Enable buffering of the data in a ring buffer", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + g_object_class_install_properties(gobject_class, N_PROPERTIES, uca_phantom_communicate_properties); } -static void uca_phantom_communicate_init(UcaPhantomCommunicate *instance) { - g_print("Initializing UcaPhantomCommunicate\n"); - +static void uca_phantom_communicate_init(UcaPhantomCommunicate* instance) +{ instance->phantom_ip = NULL; instance->phantom_xip = NULL; instance->netcard = NULL; @@ -417,9 +403,14 @@ static void uca_phantom_communicate_init(UcaPhantomCommunicate *instance) { instance->data_unpacker = NULL; instance->unpacked_queue = g_async_queue_new(); instance->request_queue = g_async_queue_new(); + instance->ts_request_queue = g_async_queue_new(); + instance->ts_queue = g_async_queue_new(); + + instance->buffering = FALSE; + instance->buffer_state = ACQUIRING; // Camera setup variables - instance->settings = (CaptureSettings){ + instance->settings = (CaptureSettings) { .sync_mode = SYNC_MODE_VIDEO_FRAME_RATE, .acquisition_mode = ACQUISITION_MODE_STANDARD, .image_format = IMG_P10, @@ -449,8 +440,9 @@ static void uca_phantom_communicate_init(UcaPhantomCommunicate *instance) { }; } -static void uca_phantom_communicate_constructed(GObject *object) { - UcaPhantomCommunicate *instance = UCA_PHANTOM_COMMUNICATE(object); +static void uca_phantom_communicate_constructed(GObject* object) +{ + UcaPhantomCommunicate* instance = UCA_PHANTOM_COMMUNICATE(object); instance->control_connection_state = DISCONNECTED; @@ -462,13 +454,11 @@ static void uca_phantom_communicate_constructed(GObject *object) { G_OBJECT_CLASS(uca_phantom_communicate_parent_class)->constructed(object); } -static void uca_phantom_communicate_dispose(GObject *object) { - UcaPhantomCommunicate *instance = UCA_PHANTOM_COMMUNICATE(object); - - g_print("Disposing UcaPhantomCommunicate\n"); +static void uca_phantom_communicate_dispose(GObject* object) +{ + UcaPhantomCommunicate* instance = UCA_PHANTOM_COMMUNICATE(object); // free the handle - if (instance->handle != NULL) - { + if (instance->handle != NULL) { pcap_close(instance->handle); } @@ -490,31 +480,35 @@ static void uca_phantom_communicate_dispose(GObject *object) { g_ptr_array_free(instance->capture_settings, TRUE); // Empty the queues, even though they should be empty - while (instance->packed_queue && g_async_queue_length(instance->packed_queue) > 0) - { - // g_print ("Whoa, there's still stuff in here !\n"); - CineData *cine_data = g_async_queue_try_pop(instance->packed_queue); + while (instance->packed_queue && g_async_queue_length(instance->packed_queue) > 0) { + CineData* cine_data = g_async_queue_try_pop(instance->packed_queue); g_free(cine_data); } - while (instance->packed_queue && g_async_queue_length(instance->unpacked_queue) > 0) - { - // g_print ("Whoa, there's still stuff in here !\n"); - CineData *cine_data = g_async_queue_try_pop(instance->unpacked_queue); + while (instance->unpacked_queue && g_async_queue_length(instance->unpacked_queue) > 0) { + CineData* cine_data = g_async_queue_try_pop(instance->unpacked_queue); g_free(cine_data); } // Disconnect the data and control streams - GError *error = NULL; - if (!g_io_stream_close(G_IO_STREAM(instance->control_connection), NULL, &error)) - { + GError* error = NULL; + if (!g_io_stream_close(G_IO_STREAM(instance->control_connection), NULL, &error)) { g_warning("Failed to close stream: %s", error->message); g_error_free(error); } - if (instance->timestamping || !instance->xenabled) - { + + if (instance->timestamping || !instance->xenabled) { + // Empty the timestamp queue + while (instance->ts_queue && g_async_queue_length(instance->ts_queue) > 0) { + TimestampData* ts_data = g_async_queue_try_pop(instance->ts_queue); + g_free(ts_data); + } + // Empty the timestamp request queue + while (instance->ts_request_queue && g_async_queue_length(instance->ts_request_queue) > 0) { + TsRequest* ts_request = g_async_queue_try_pop(instance->ts_request_queue); + g_free(ts_request); + } // TODO: check this function - if (instance->data_connection && !g_io_stream_close(G_IO_STREAM(instance->data_connection), NULL, &error)) - { + if (instance->data_connection && !g_io_stream_close(G_IO_STREAM(instance->data_connection), NULL, &error)) { g_warning("Failed to close stream: %s", error->message); g_error_free(error); } @@ -523,46 +517,44 @@ static void uca_phantom_communicate_dispose(GObject *object) { G_OBJECT_CLASS(uca_phantom_communicate_parent_class)->dispose(object); } -static void uca_phantom_communicate_finalize(GObject *object) { - UcaPhantomCommunicate *instance = UCA_PHANTOM_COMMUNICATE(object); +static void uca_phantom_communicate_finalize(GObject* object) +{ + UcaPhantomCommunicate* instance = UCA_PHANTOM_COMMUNICATE(object); g_socket_service_stop(instance->service); - g_print("Finalizing UcaPhantomCommunicate\n"); - // Free control connection resources - if (G_IS_SOCKET_CLIENT(instance->control_client)) - { + if (G_IS_SOCKET_CLIENT(instance->control_client)) { g_object_unref(instance->control_client); } - if (G_IS_SOCKET_CONNECTION(instance->control_connection)) - { + if (G_IS_SOCKET_CONNECTION(instance->control_connection)) { g_object_unref(instance->control_connection); } // Free data connection resources - if (G_IS_SOCKET_SERVICE(instance->service)) - { + if (G_IS_SOCKET_SERVICE(instance->service)) { g_object_unref(instance->service); } - if (G_IS_SOCKET_CONNECTION(instance->data_connection)) - { + if (G_IS_SOCKET_CONNECTION(instance->data_connection)) { g_object_unref(instance->data_connection); } // Free the 10GbE resources - if (instance->packed_queue != NULL) - { + if (instance->packed_queue != NULL) { g_async_queue_unref(instance->packed_queue); } - if (instance->unpacked_queue != NULL) - { + if (instance->unpacked_queue != NULL) { g_async_queue_unref(instance->unpacked_queue); } - if (instance->request_queue != NULL) - { + if (instance->request_queue != NULL) { g_async_queue_unref(instance->request_queue); } + if (instance->ts_queue != NULL) { + g_async_queue_unref(instance->ts_queue); + } + if (instance->ts_request_queue != NULL) { + g_async_queue_unref(instance->ts_request_queue); + } G_OBJECT_CLASS(uca_phantom_communicate_parent_class)->finalize(object); } @@ -570,55 +562,108 @@ static void uca_phantom_communicate_finalize(GObject *object) { /* * Private class definitions */ -static void uca_phantom_communicate_set_phantom_ip(UcaPhantomCommunicate *self, const gchar *property) { +static void uca_phantom_communicate_set_phantom_ip(UcaPhantomCommunicate* self, const gchar* property) +{ g_free(self->phantom_ip); self->phantom_ip = g_strdup(property); } -static void uca_phantom_communicate_set_phantom_xip(UcaPhantomCommunicate *self, const gchar *property) { +static void uca_phantom_communicate_set_phantom_xip(UcaPhantomCommunicate* self, const gchar* property) +{ g_free(self->phantom_xip); self->phantom_xip = g_strdup(property); } -static void uca_phantom_communicate_set_netcard_ip(UcaPhantomCommunicate *self, const gchar *property) { +static void uca_phantom_communicate_set_netcard_ip(UcaPhantomCommunicate* self, const gchar* property) +{ g_free(self->netcard_ip); self->netcard_ip = g_strdup(property); } -static void uca_phantom_communicate_set_netcard_xip(UcaPhantomCommunicate *self, const gchar *property) { +static void uca_phantom_communicate_set_netcard_xip(UcaPhantomCommunicate* self, const gchar* property) +{ g_free(self->netcard_xip); self->netcard_xip = g_strdup(property); } -static void uca_phantom_communicate_set_netcard(UcaPhantomCommunicate *self, const gchar *property) { +static void uca_phantom_communicate_set_netcard(UcaPhantomCommunicate* self, const gchar* property) +{ g_free(self->netcard); self->netcard = g_strdup(property); } -static void uca_phantom_communicate_set_xnetcard(UcaPhantomCommunicate *self, const gchar *property) { +static void uca_phantom_communicate_set_xnetcard(UcaPhantomCommunicate* self, const gchar* property) +{ g_free(self->xnetcard); self->xnetcard = g_strdup(property); } -static void uca_phantom_communicate_set_xenabled(UcaPhantomCommunicate *self, gboolean property) { self->xenabled = property; } -static void uca_phantom_communicate_set_control_port(UcaPhantomCommunicate *self, guint property) { self->control_port = property; } -static void uca_phantom_communicate_set_ip_source(UcaPhantomCommunicate *self, guint property) { self->phantom_ipsource = property; } -static void uca_phantom_communicate_set_timestamping(UcaPhantomCommunicate *self, gboolean property) { self->timestamping = property; } - -static gchar *uca_phantom_communicate_get_phantom_ip(UcaPhantomCommunicate *self) { return self->phantom_ip; } -static gchar *uca_phantom_communicate_get_phantom_xip(UcaPhantomCommunicate *self) { return self->phantom_xip; } -static gchar *uca_phantom_communicate_get_netcard_ip(UcaPhantomCommunicate *self) { return self->netcard_ip; } -static gchar *uca_phantom_communicate_get_netcard_xip(UcaPhantomCommunicate *self) { return self->netcard_xip; } -static gchar *uca_phantom_communicate_get_netcard(UcaPhantomCommunicate *self) { return self->netcard; } -static gchar *uca_phantom_communicate_get_xnetcard(UcaPhantomCommunicate *self) { return self->xnetcard; } -static gboolean uca_phantom_communicate_get_xenabled(UcaPhantomCommunicate *self) { return self->xenabled; } -static guint uca_phantom_communicate_get_control_port(UcaPhantomCommunicate *self) { return self->control_port; } -static guint uca_phantom_communicate_get_ip_source(UcaPhantomCommunicate *self) { return self->phantom_ipsource; } -static guint uca_phantom_communicate_get_timestamping(UcaPhantomCommunicate *self) { return self->timestamping; } - -static void uca_phantom_communicate_set_property( - GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) { - UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE(object); - - switch (property_id) - { +static void uca_phantom_communicate_set_xenabled(UcaPhantomCommunicate* self, gboolean property) +{ + self->xenabled = property; +} +static void uca_phantom_communicate_set_control_port(UcaPhantomCommunicate* self, guint property) +{ + self->control_port = property; +} +static void uca_phantom_communicate_set_ip_source(UcaPhantomCommunicate* self, guint property) +{ + self->phantom_ipsource = property; +} +static void uca_phantom_communicate_set_timestamping(UcaPhantomCommunicate* self, gboolean property) +{ + self->timestamping = property; +} +static void uca_phantom_communicate_set_buffering (UcaPhantomCommunicate* self, gboolean property) +{ + self->buffering = property; +} + +static gchar* uca_phantom_communicate_get_phantom_ip(UcaPhantomCommunicate* self) +{ + return self->phantom_ip; +} +static gchar* uca_phantom_communicate_get_phantom_xip(UcaPhantomCommunicate* self) +{ + return self->phantom_xip; +} +static gchar* uca_phantom_communicate_get_netcard_ip(UcaPhantomCommunicate* self) +{ + return self->netcard_ip; +} +static gchar* uca_phantom_communicate_get_netcard_xip(UcaPhantomCommunicate* self) +{ + return self->netcard_xip; +} +static gchar* uca_phantom_communicate_get_netcard(UcaPhantomCommunicate* self) +{ + return self->netcard; +} +static gchar* uca_phantom_communicate_get_xnetcard(UcaPhantomCommunicate* self) +{ + return self->xnetcard; +} +static gboolean uca_phantom_communicate_get_xenabled(UcaPhantomCommunicate* self) +{ + return self->xenabled; +} +static guint uca_phantom_communicate_get_control_port(UcaPhantomCommunicate* self) +{ + return self->control_port; +} +static guint uca_phantom_communicate_get_ip_source(UcaPhantomCommunicate* self) +{ + return self->phantom_ipsource; +} +static gboolean uca_phantom_communicate_get_timestamping(UcaPhantomCommunicate* self) +{ + return self->timestamping; +} +static gboolean uca_phantom_communicate_get_buffering(UcaPhantomCommunicate* self) +{ + return self->buffering; +} + +static void uca_phantom_communicate_set_property(GObject* object, guint property_id, const GValue* value, + GParamSpec* pspec) +{ + UcaPhantomCommunicate* self = UCA_PHANTOM_COMMUNICATE(object); + + switch (property_id) { case PROP_PHANTOM_IP: uca_phantom_communicate_set_phantom_ip(self, g_value_get_string(value)); break; @@ -649,21 +694,20 @@ static void uca_phantom_communicate_set_property( case PROP_TIMESTAMPING: uca_phantom_communicate_set_timestamping(self, g_value_get_boolean(value)); break; + case PROP_BUFFERING: + uca_phantom_communicate_set_buffering(self, g_value_get_boolean(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } -static void uca_phantom_communicate_get_property( - GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) { - UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE(object); +static void uca_phantom_communicate_get_property(GObject* object, guint property_id, GValue* value, GParamSpec* pspec) +{ + UcaPhantomCommunicate* self = UCA_PHANTOM_COMMUNICATE(object); - switch (property_id) - { + switch (property_id) { case PROP_PHANTOM_IP: g_value_set_string(value, uca_phantom_communicate_get_phantom_ip(self)); break; @@ -694,24 +738,28 @@ static void uca_phantom_communicate_get_property( case PROP_TIMESTAMPING: g_value_set_boolean(value, uca_phantom_communicate_get_timestamping(self)); break; + case PROP_BUFFERING: + g_value_set_boolean(value, uca_phantom_communicate_get_buffering(self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } -static gchar * -uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc) { +static gchar* uca_phantom_communicate_discover(UcaPhantomCommunicate* self, GError** error_loc) +{ // Note: find a way to do this without a goto statement. - g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, NULL); // verify that error_loc is not set + g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, + NULL); // verify that error_loc is not set - GError *sub_error = NULL; - GError *phantom_error = NULL; - GSocket *socket = NULL; - GMatchInfo *info = NULL; - GSocketAddress *remote_socket_addr = NULL; - GSocketAddress *result = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; + GSocket* socket = NULL; + GMatchInfo* info = NULL; + GSocketAddress* remote_socket_addr = NULL; + GSocketAddress* result = NULL; const gchar request[] = "phantom?"; const gchar pattern[] = "PH16 (\\d+) (\\d+) (\\d+)"; gint FLAG = ALL; @@ -723,12 +771,12 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc g_message("Attempting to discover the phantom...\n"); - const gchar *bcast_address = "100.100.255.255"; - GSocketAddress *bcast_socket_addr = g_inet_socket_address_new_from_string(bcast_address, port); + const gchar* bcast_address = "100.100.255.255"; + GSocketAddress* bcast_socket_addr = g_inet_socket_address_new_from_string(bcast_address, port); - if (bcast_socket_addr == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_BCAST_ADDR, "Failed to parse broadcasting address '%s' on port '%d'\n", bcast_address, port); + if (bcast_socket_addr == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_BCAST_ADDR, + "Failed to parse broadcasting address '%s' on port '%d'\n", bcast_address, port); g_propagate_error(error_loc, phantom_error); FLAG = BCAST; @@ -737,15 +785,13 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &sub_error); - if (socket == NULL) - { - if (sub_error == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SOCKET, "Failed to create socket\n"); - } - else - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SOCKET, "Failed to create socket: %s\n", sub_error->message); + if (socket == NULL) { + if (sub_error == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SOCKET, + "Failed to create socket\n"); + } else { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SOCKET, + "Failed to create socket: %s\n", sub_error->message); g_error_free(sub_error); } g_propagate_error(error_loc, phantom_error); @@ -757,15 +803,13 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc gssize wrote = g_socket_send_to(socket, bcast_socket_addr, request, strlen(request), NULL, &sub_error); - if (wrote < -1) - { - if (sub_error == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SEND, "Failed to send broadcast\n"); - } - else - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SEND, "Failed to send broadcast: %s\n", sub_error->message); + if (wrote < -1) { + if (sub_error == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SEND, + "Failed to send broadcast\n"); + } else { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SEND, + "Failed to send broadcast: %s\n", sub_error->message); g_error_free(sub_error); } g_propagate_error(error_loc, phantom_error); @@ -777,15 +821,13 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc gssize received = g_socket_receive_from(socket, &remote_socket_addr, reply, strlen(reply), NULL, &sub_error); g_print("received: %ld\n", received); - if (received < -1) - { - if (sub_error == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RECEIVE, "Failed to receive broadcast\n"); - } - else - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RECEIVE, "Failed to receive broadcast: %s\n", sub_error->message); + if (received < -1) { + if (sub_error == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RECEIVE, + "Failed to receive broadcast\n"); + } else { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RECEIVE, + "Failed to receive broadcast: %s\n", sub_error->message); g_error_free(sub_error); } g_propagate_error(error_loc, phantom_error); @@ -794,17 +836,15 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc goto cleanup; } - GRegex *regex = g_regex_new(pattern, 0, 0, &sub_error); + GRegex* regex = g_regex_new(pattern, 0, 0, &sub_error); - if (regex == NULL) - { - if (sub_error == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, "Failed to create regex\n"); - } - else - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, "Failed to create regex: %s\n", sub_error->message); + if (regex == NULL) { + if (sub_error == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, + "Failed to create regex\n"); + } else { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, + "Failed to create regex: %s\n", sub_error->message); g_error_free(sub_error); } g_propagate_error(error_loc, phantom_error); @@ -815,20 +855,20 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc gboolean matched = g_regex_match(regex, reply, 0, &info); - if (!matched) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, "Reply '%s' does not match expected pattern.\n", reply); + if (!matched) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, + "Reply '%s' does not match expected pattern.\n", reply); g_propagate_error(error_loc, phantom_error); FLAG = ALL; goto cleanup; } - gchar *port_string = g_match_info_fetch(info, 1); + gchar* port_string = g_match_info_fetch(info, 1); - if (port_string == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, "Regex pattern number 1 not found.\n"); + if (port_string == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REGEX, + "Regex pattern number 1 not found.\n"); g_propagate_error(error_loc, phantom_error); FLAG = ALL; @@ -839,15 +879,14 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc guint accept_port = atoi(port_string); g_free(port_string); - result = g_inet_socket_address_new(g_inet_socket_address_get_address((GInetSocketAddress *)remote_socket_addr), port); + result = g_inet_socket_address_new(g_inet_socket_address_get_address((GInetSocketAddress*)remote_socket_addr), port); - gchar *ip_address = g_inet_address_to_string(g_inet_socket_address_get_address((GInetSocketAddress *)result)); + gchar* ip_address = g_inet_address_to_string(g_inet_socket_address_get_address((GInetSocketAddress*)result)); g_message("Phantom found on port %d with the IPV4 address: %s.\n", port, ip_address); cleanup: - switch (FLAG) - { + switch (FLAG) { case ALL: g_match_info_free(info); /* fall through */ @@ -877,55 +916,44 @@ uca_phantom_communicate_discover(UcaPhantomCommunicate *self, GError **error_loc * The #UcaPhantomCommunicate struct contains only private data and should * only be accessed using the provided API. */ -static gboolean -uca_phantom_communicate(UcaPhantomCommunicate *self, PhantomRequest *request, PhantomReply *reply, GError **error_loc) { +static gboolean uca_phantom_communicate(UcaPhantomCommunicate* self, PhantomRequest* request, PhantomReply* reply, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(request != NULL || reply != NULL, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); g_return_val_if_fail(G_IS_INPUT_STREAM(self->input_controlstream), FALSE); g_return_val_if_fail(G_IS_OUTPUT_STREAM(self->output_controlstream), FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; - request->write_size = g_output_stream_write( - self->output_controlstream, - request->message, - request->size, - NULL, - &sub_error); + request->write_size = g_output_stream_write(self->output_controlstream, request->message, request->size, NULL, &sub_error); - if (request->write_size < -1) - { + if (request->write_size < -1) { g_warning("Failed to write request to control output stream: %s\n", sub_error->message); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SEND, "Failed to write request to control output stream: %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SEND, + "Failed to write request to control output stream: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } - reply->read_size = g_input_stream_read( - self->input_controlstream, - reply->raw, - reply->size, - NULL, - &sub_error); + reply->read_size = g_input_stream_read(self->input_controlstream, reply->raw, reply->size, NULL, &sub_error); - if (reply->read_size < -1) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RECEIVE, "Failed to read reply from control input stream: %s\n", sub_error->message); + if (reply->read_size < -1) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RECEIVE, + "Failed to read reply from control input stream: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; - } - else if (reply->read_size == 0) - { + } else if (reply->read_size == 0) { g_warning("Reached EOF on stream.\n"); } - g_debug("raw: %s\n", reply->raw); + g_debug("uca_phantom_communicate: %s\n", reply->raw); g_output_stream_flush(self->output_controlstream, NULL, NULL); @@ -936,21 +964,21 @@ uca_phantom_communicate(UcaPhantomCommunicate *self, PhantomRequest *request, Ph * Public methods * */ -UcaPhantomCommunicate *uca_phantom_communicate_new(void) { +UcaPhantomCommunicate* uca_phantom_communicate_new(void) +{ return g_object_new(UCA_TYPE_PHANTOM_COMMUNICATE, NULL); } -gboolean uca_phantom_communicate_connect_controlstream(UcaPhantomCommunicate *self, GError **error_loc) { +gboolean uca_phantom_communicate_connect_controlstream(UcaPhantomCommunicate* self, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(self->control_connection_state == DISCONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; - gchar *ip_address = NULL; - ; + GError* sub_error = NULL; + GError* phantom_error = NULL; + gchar* ip_address = NULL; - switch (self->phantom_ipsource) - { + switch (self->phantom_ipsource) { case USE_CLASS: ip_address = g_strdup(self->phantom_ip); break; @@ -959,46 +987,37 @@ gboolean uca_phantom_communicate_connect_controlstream(UcaPhantomCommunicate *se break; default: g_warning("Invalid ip source.\n"); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, "Invalid ip source.\n"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, + "Invalid ip source.\n"); g_propagate_error(error_loc, phantom_error); return FALSE; break; } - // print the server address - g_print("Server address: %s, %d\n", ip_address, self->control_port); - // /* resolve the server address */ - // server_address = g_inet_socket_address_new_from_string(ip_address, self->control_port); - // if (!server_address) { + // server_address = g_inet_socket_address_new_from_string(ip_address, + // self->control_port); if (!server_address) { // g_print("Invalid address!\n"); // return 1; // } - self->control_connection = g_socket_client_connect_to_host( - self->control_client, - ip_address, - self->control_port, - NULL, - &sub_error); + self->control_connection = g_socket_client_connect_to_host(self->control_client, ip_address, self->control_port, NULL, &sub_error); g_message("Connected to server!\n"); g_free(ip_address); - if (self->control_connection == NULL) - { - - if (sub_error != NULL) - { + if (self->control_connection == NULL) { + if (sub_error != NULL) { g_warning("Could not connect to the phantom: %s\n", sub_error->message); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, "Could not connect to the phantom: %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, + "Could not connect to the phantom: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); - } - else - { + } else { g_warning("Could not connect to the phantom.\n"); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, "Could not connect to the phantom: Unknown error when attempting to connect.\n"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, + "Could not connect to the phantom: Unknown error when " + "attempting to connect.\n"); g_propagate_error(error_loc, phantom_error); } @@ -1010,28 +1029,28 @@ gboolean uca_phantom_communicate_connect_controlstream(UcaPhantomCommunicate *se self->output_controlstream = g_io_stream_get_output_stream(G_IO_STREAM(self->control_connection)); self->input_controlstream = g_io_stream_get_input_stream(G_IO_STREAM(self->control_connection)); - if (!G_IS_OUTPUT_STREAM(self->output_controlstream) || !G_IS_INPUT_STREAM(self->input_controlstream)) - { + if (!G_IS_OUTPUT_STREAM(self->output_controlstream) || !G_IS_INPUT_STREAM(self->input_controlstream)) { g_warning("Could not get control streams.\n"); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, "Could not get control streams.\n"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, + "Could not get control streams.\n"); g_propagate_error(error_loc, phantom_error); return FALSE; } // Get the remote address of the connection - GSocketAddress *remote_address = g_socket_connection_get_remote_address(self->control_connection, &sub_error); - if (sub_error != NULL || remote_address == NULL) - { + GSocketAddress* remote_address = g_socket_connection_get_remote_address(self->control_connection, &sub_error); + if (sub_error != NULL || remote_address == NULL) { g_warning("Could not get remote address: %s\n", sub_error->message); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, "Could not get remote address: %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, + "Could not get remote address: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); } - GSocketAddress *local_address = g_socket_connection_get_local_address(self->control_connection, &sub_error); - if (sub_error != NULL || local_address == NULL) - { + GSocketAddress* local_address = g_socket_connection_get_local_address(self->control_connection, &sub_error); + if (sub_error != NULL || local_address == NULL) { g_warning("Could not get local address: %s\n", sub_error->message); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, "Could not get local address: %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT, + "Could not get local address: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); } @@ -1039,13 +1058,14 @@ gboolean uca_phantom_communicate_connect_controlstream(UcaPhantomCommunicate *se guint remote_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(remote_address)); guint local_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(local_address)); - GInetAddress *remote_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(remote_address)); - GInetAddress *local_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(local_address)); + GInetAddress* remote_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(remote_address)); + GInetAddress* local_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(local_address)); - gchar *remote_ip_address = g_inet_address_to_string(remote_inet_address); - gchar *local_ip_address = g_inet_address_to_string(local_inet_address); + gchar* remote_ip_address = g_inet_address_to_string(remote_inet_address); + gchar* local_ip_address = g_inet_address_to_string(local_inet_address); - g_message("Control connection established with phantom (%s:%d) -> (%s:%d)\n", local_ip_address, local_port, remote_ip_address, remote_port); + g_message("Control connection established with phantom (%s:%d) -> (%s:%d)\n", local_ip_address, local_port, + remote_ip_address, remote_port); g_object_unref(remote_address); g_object_unref(local_address); @@ -1056,88 +1076,54 @@ gboolean uca_phantom_communicate_connect_controlstream(UcaPhantomCommunicate *se return TRUE; } -/** - * @brief Send a command to the phantom and get the reply - * - * @paragraph This variadic function is used to send a command to the phantom and get the reply. - * The command is specified by the command_flag. The reply is stored in the caller-owned reply struct. - * - * @param self - * @param command_flag - * @param reply - * @param error_loc - * @param ... - * @return gboolean - * - * TODO: handle the variadic arguments better (i.e. format them into a string that fits the phantom's format) - */ -gboolean uca_phantom_communicate_run_command(UcaPhantomCommunicate *self, guint command_flag, PhantomReply *reply, GError **error_loc, ...){ +gboolean uca_phantom_communicate_run_command(UcaPhantomCommunicate* self, guint command_flag, gchar* command_arg, + PhantomReply* reply, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(command_flag < N_UNIT_COMMANDS, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; - // Setup the request - PhantomRequest request = { - .command = Commands[command_flag], - .message = NULL, - .size = 0, - .write_size = 0}; - - // Setup args of command - va_list va_args; - guint nb_args = 0; - guint nb_arg_max = Commands[command_flag].argc; - gchar *next_arg = NULL; - const gchar *args[nb_arg_max]; - - va_start(va_args, error_loc); - while ((next_arg = va_arg(va_args, gchar *)) != NULL) { - nb_args++; - if (nb_args > nb_arg_max) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, "Too many arguments for command %s. Expected %d, got %d.\n", Commands[command_flag].name, Commands[command_flag].argc, nb_args); - g_propagate_error(error_loc, phantom_error); - return FALSE; - } - args[nb_args - 1] = next_arg; + gboolean reply_is_local = FALSE; + + // If the reply is null, just make a new one + if (reply == NULL) { + reply_is_local = TRUE; + reply = g_new0(PhantomReply, 1); } - va_end(va_args); - gsize args_len = 0; - for (guint i = 0; i < nb_args; i++) { - args_len += strlen(args[i]); + // Setup the request + PhantomRequest request = { .command = Commands[command_flag], .message = NULL, .size = 0, .write_size = 0 }; + + if (command_arg == NULL) { + command_arg = ""; } - // Manually build the message to ensure that the string is correclty NULL-ended - request.size = (strlen(request.command.name) + request.command.argc + args_len + strlen("\r\n")) * sizeof(request.message); - request.message = g_malloc0(request.size); + // Manually build the message to ensure that the string is correclty + // CRLF-ended + request.size = (strlen(request.command.name) + 1 + strlen(command_arg) + strlen("\r\n")) * sizeof(gchar); + request.message = g_strconcat(request.command.name, " ", command_arg, "\r\n", NULL); - if (request.message == NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, "Could not allocate and assemble request message. Fatal error.\n"); + if (request.message == NULL || strlen(request.message) != request.size) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, + "Could not allocate and assemble request message. Fatal error.\n"); g_propagate_error(error_loc, phantom_error); return FALSE; } - g_strlcat(request.message, request.command.name, request.size); - for (guint i = 0; i < nb_args; i++) { - g_strlcat(request.message, " ", request.size); - g_strlcat(request.message, args[i], request.size); - } - g_strlcat(request.message, "\r\n", request.size); - // Setup the reply - *reply = (PhantomReply){ - .raw = NULL, - .size = 512, + *reply = (PhantomReply) { .raw = NULL, + .size = 65536, // ref : v16.0.0 .value = G_VALUE_INIT, - .read_size = 0}; + .read_size = 0 }; reply->raw = g_malloc0(reply->size); if (reply->raw == NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, "Could not allocate reply message. Fatal error.\n"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, + "Could not allocate reply message. Fatal error.\n"); g_propagate_error(error_loc, phantom_error); g_free(request.message); return FALSE; @@ -1152,7 +1138,8 @@ gboolean uca_phantom_communicate_run_command(UcaPhantomCommunicate *self, guint request.message = NULL; if (!communicated && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, "Failed to run command %s: %s\n", request.command.name, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, + "Failed to run command %s: %s\n", request.command.name, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); @@ -1161,12 +1148,15 @@ gboolean uca_phantom_communicate_run_command(UcaPhantomCommunicate *self, guint } if (communicated && sub_error != NULL) { - g_warning("Successfully ran command %s. However, an error occured: %s\n", request.command.name, sub_error->message); + g_warning("Successfully ran command %s. However, an error occured: %s\n", request.command.name, + sub_error->message); g_clear_error(&sub_error); } if (g_str_has_prefix(reply->raw, "ERR:") == TRUE) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, "Phantom returned an error when running command '%s': \n\t> %s\n", request.command.name, reply->raw); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, + "Phantom returned an error when running command '%s': \n\t> %s\n", request.command.name, + reply->raw); g_propagate_error(error_loc, phantom_error); g_free(reply->raw); reply->raw = NULL; @@ -1174,58 +1164,53 @@ gboolean uca_phantom_communicate_run_command(UcaPhantomCommunicate *self, guint return FALSE; } + if (reply_is_local == TRUE) { + g_free(reply->raw); + reply->raw = NULL; + g_free(reply); + reply = NULL; + } + return TRUE; } -/** - * @brief Get the value of a variable from the phantom - * - * @paragraph This function will get the value of a unit variable from the phantom using the uca_phantom_communicate_run_command function. - * - * @param self - * @param variable_flag - * @param return_value - * @param error_loc - * @return gboolean - */ -gboolean uca_phantom_communicate_get_variable(UcaPhantomCommunicate *self, guint variable_flag, GValue *return_value, GError **error_loc) { +gboolean uca_phantom_communicate_get_variable(UcaPhantomCommunicate* self, guint variable_flag, GValue* return_value, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(variable_flag < N_UNIT_PROPERTIES, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; gchar pattern[] = "\\s:\\s"; PhantomReply reply; - gchar *name = NULL; + gchar* name = NULL; gboolean res = FALSE; // Check if the command is between CT_STATE and CT_META_GPS - if (variable_flag >= UNIT_CT_STATE && variable_flag <= UNIT_CT_META_GPS) - { + if (variable_flag >= UNIT_CT_STATE && variable_flag <= UNIT_CT_META_GPS) { name = g_strdup_printf(variables[variable_flag].name, self->settings.current_cine); - res = uca_phantom_communicate_run_command(self, CMD_GET, &reply, &sub_error, name, NULL); + res = uca_phantom_communicate_run_command(self, CMD_GET, name, &reply, &sub_error); g_free(name); - } - else - { - res = uca_phantom_communicate_run_command(self, CMD_GET, &reply, &sub_error, variables[variable_flag].name, NULL); + } else { + res = uca_phantom_communicate_run_command(self, CMD_GET, variables[variable_flag].name, &reply, &sub_error); } - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, "Failed to get variable %s: %s\n", variables[variable_flag].name, sub_error->message); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, + "Failed to get variable %s: %s\n", variables[variable_flag].name, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } // Extract the actual data from the raw reply - GRegex *regex = g_regex_new(pattern, 0, 0, &sub_error); + GRegex* regex = g_regex_new(pattern, 0, 0, &sub_error); - if (regex == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, "Failed to create regex object. Aborting...: %s\n", sub_error->message); + if (regex == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, + "Failed to create regex object. Aborting...: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_error_free(sub_error); @@ -1233,17 +1218,28 @@ gboolean uca_phantom_communicate_get_variable(UcaPhantomCommunicate *self, guint return FALSE; } - gchar **matched = g_regex_split(regex, reply.raw, 0); + gchar** matched = g_regex_split(regex, reply.raw, 0); g_return_val_if_fail(matched != NULL, FALSE); - gchar *prefix = matched[0]; - gchar *suffix = matched[1]; + gchar* prefix = matched[0]; + gchar* suffix = matched[1]; + + if (suffix == NULL || prefix == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, + "Failed to extract variable value from reply: %s\n", reply.raw); + g_propagate_error(error_loc, phantom_error); + + g_free(reply.raw); + g_strfreev(matched); + g_regex_unref(regex); + return FALSE; + } // Check for error mesage from phantom - if (g_str_has_prefix(prefix, "ERR")) - { + if (g_str_has_prefix(prefix, "ERR")) { g_warning("Invalid phantom command: %s\n", reply.raw); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, "Invalid phantom command: %s\n", reply.raw); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, + "Invalid phantom command: %s\n", reply.raw); g_propagate_error(error_loc, phantom_error); g_free(reply.raw); @@ -1256,8 +1252,7 @@ gboolean uca_phantom_communicate_get_variable(UcaPhantomCommunicate *self, guint g_value_init(return_value, variables[variable_flag].type); // Use Gvalue container to store it - switch (variables[variable_flag].type) - { + switch (variables[variable_flag].type) { case G_TYPE_STRING: g_value_set_string(return_value, suffix); break; @@ -1289,56 +1284,56 @@ gboolean uca_phantom_communicate_get_variable(UcaPhantomCommunicate *self, guint return TRUE; } -/** - * @brief Set the value of a variable on the phantom - * - * @param self - * @param variable_flag - * @param value - * @param error_loc - * @return gboolean - */ -gboolean uca_phantom_communicate_set_variable(UcaPhantomCommunicate *self, guint variable_flag, const gchar *value, GError **error_loc) { +gboolean uca_phantom_communicate_set_variable(UcaPhantomCommunicate* self, guint variable_flag, const gchar* value, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(variable_flag < N_UNIT_PROPERTIES, FALSE); g_return_val_if_fail(value != NULL, FALSE); g_return_val_if_fail(variables[variable_flag].flags & G_PARAM_WRITABLE, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; PhantomReply reply; - gboolean res = uca_phantom_communicate_run_command(self, CMD_SET, &reply, &sub_error, variables[variable_flag].name, value, NULL); + gchar* arg = g_strdup_printf("%s %s", variables[variable_flag].name, value); + gboolean res = uca_phantom_communicate_run_command(self, CMD_SET, arg, &reply, &sub_error); - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_VARIABLE, "Failed to set variable '%s':\n\t> %s\n", variables[variable_flag].name, sub_error->message); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_VARIABLE, + "Failed to set variable '%s':\n\t> %s\n", variables[variable_flag].name, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } g_free(reply.raw); reply.raw = NULL; + g_free(arg); + arg = NULL; return TRUE; } -static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate *self, guint16 *width, guint16 *height, GError **error_loc) { +static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate* self, guint16* width, guint16* height, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; - const gchar *pattern = "([0-9]+)\\sx\\s([0-9]+)"; + GError* sub_error = NULL; + GError* phantom_error = NULL; + const gchar* pattern = "([0-9]+)\\sx\\s([0-9]+)"; GValue resolution = G_VALUE_INIT; - const gchar *reply = NULL; + const gchar* reply = NULL; gboolean res = uca_phantom_communicate_get_variable(self, UNIT_DEFC_RES, &resolution, &sub_error); - if (res != TRUE && sub_error != NULL) - { - g_print("Failed to get resolution:\n\t> %s\n", sub_error->message); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_RESOLUTION, + "Failed to get resolution from phantom. Aborting...: %s\n", sub_error->message); + g_propagate_error(error_loc, phantom_error); + g_error_free(sub_error); g_clear_error(&sub_error); return FALSE; } @@ -1346,25 +1341,25 @@ static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate *se reply = g_value_get_string(&resolution); // Extract the actual data from the raw reply - GRegex *regex = g_regex_new(pattern, 0, 0, &sub_error); + GRegex* regex = g_regex_new(pattern, 0, 0, &sub_error); - if (regex == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_RESOLUTION, "Failed to create regex object. Aborting...: %s\n", sub_error->message); + if (regex == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_RESOLUTION, + "Failed to create regex object. Aborting...: %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_error_free(sub_error); return FALSE; } - gchar **matched = g_regex_split(regex, reply, 0); + gchar** matched = g_regex_split(regex, reply, 0); g_return_val_if_fail(matched != NULL, FALSE); // Check for error mesage from phantom - if (g_str_has_prefix(matched[0], "ERR")) - { + if (g_str_has_prefix(matched[0], "ERR")) { g_warning("Invalid phantom command: %s\n", reply); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, "Invalid phantom command: %s\n", reply); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_VARIABLE, + "Invalid phantom command: %s\n", reply); g_propagate_error(error_loc, phantom_error); g_strfreev(matched); @@ -1372,8 +1367,8 @@ static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate *se return FALSE; } - gchar *width_s = matched[1]; - gchar *height_s = matched[2]; + gchar* width_s = matched[1]; + gchar* height_s = matched[2]; *width = g_ascii_strtoull(width_s, NULL, 10); *height = g_ascii_strtoull(height_s, NULL, 10); @@ -1387,11 +1382,13 @@ static gboolean uca_phantom_communicate_get_resolution(UcaPhantomCommunicate *se return TRUE; } -guint uca_phantom_communicate_get_pixel_width(UcaPhantomCommunicate *self) { +guint uca_phantom_communicate_get_pixel_width(UcaPhantomCommunicate* self) +{ g_return_val_if_fail(self->control_connection_state == CONNECTED, 0); return self->settings.sensor_pixel_width; } -guint uca_phantom_communicate_get_pixel_height(UcaPhantomCommunicate *self) { +guint uca_phantom_communicate_get_pixel_height(UcaPhantomCommunicate* self) +{ g_return_val_if_fail(self->control_connection_state == CONNECTED, 0); return self->settings.sensor_pixel_height; } @@ -1399,25 +1396,51 @@ guint uca_phantom_communicate_get_pixel_height(UcaPhantomCommunicate *self) { /** * Print the current capture settings */ -void uca_phantom_communicate_print_settings(UcaPhantomCommunicate *self) { +void uca_phantom_communicate_print_settings(UcaPhantomCommunicate* self) +{ g_return_if_fail(self != NULL); g_return_if_fail(self->control_connection_state == CONNECTED); } /** - * @brief Set the capture settings object + * @brief Helpher function to set the number of cines. * - * @param self - * @param settings - * @param error_loc - * @return gboolean + * This function sets the number of cines in the Phantom camera using + * set_variable function. + * + * @param self A pointer to the UcaPhantomCommunicate object. + * @param nb_cines The number of cines to set. + * @param error_loc A pointer to a GError object to store any errors that occur. + * + * @return TRUE if the number of cines was set successfully, FALSE otherwise. */ -gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, CaptureSettings *ext_settings, GError **error_loc) { +gboolean uca_phantom_communicate_set_nb_cines(UcaPhantomCommunicate* self, guint nb_cines, GError** error_loc) +{ + // Set the number of cines as well + GError* sub_error = NULL; + GError* phantom_error = NULL; + gchar* arg = g_strdup_printf("%d", nb_cines); + + gboolean res = uca_phantom_communicate_run_command(self, CMD_PARTITION_CINE_MEMORY, arg, NULL, &sub_error); + g_free(arg); + if (res != TRUE) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_NB_CINES, + "Failed to set number of cines: %s\n", sub_error->message); + g_propagate_error(error_loc, phantom_error); + g_clear_error(&sub_error); + return FALSE; + } + return TRUE; +} + +gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate* self, CaptureSettings* ext_settings, + GError** error_loc) +{ g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); static gboolean first_call = TRUE; - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; PhantomReply reply; gboolean res; @@ -1438,11 +1461,10 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu self->settings.image_format = ext_settings->image_format; self->settings.sensor_bit_depth = ext_settings->sensor_bit_depth; // is set when requesting the images } - if (ext_settings->timestamp_format != self->settings.timestamp_format || first_call) { - self->settings.timestamp_format = ext_settings->timestamp_format; - } + if (ext_settings->focal_length != self->settings.focal_length || first_call) { - g_message("Focal cannot be set by software\n"); // Focal length can only be set manually + g_message("Focal cannot be set by software\n"); // Focal length can only be + // set manually } if (ext_settings->nb_pre_trigger_frames != self->settings.nb_pre_trigger_frames || first_call) { self->settings.nb_pre_trigger_frames = ext_settings->nb_pre_trigger_frames; // is set when requesting the images @@ -1451,13 +1473,33 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu self->settings.current_cine = ext_settings->current_cine; // is set when requesting the images } + if (ext_settings->timestamp_format != self->settings.timestamp_format || first_call) { + self->settings.timestamp_format = ext_settings->timestamp_format; + if (self->settings.timestamp_format != TS_NONE) { + self->timestamping = TRUE; + } else { + self->timestamping = FALSE; + } + res = uca_phantom_communicate_set_variable( + self, UNIT_CAM_TSFORMAT, TimestampSpecs[self->settings.timestamp_format].format_string, &sub_error); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set timestamp format to %d:\n\t> %s\n", ext_settings->timestamp_format, + sub_error->message); + g_propagate_error(error_loc, phantom_error); + g_clear_error(&sub_error); + return FALSE; + } + } + if (ext_settings->sync_mode != self->settings.sync_mode || first_call) { self->settings.sync_mode = ext_settings->sync_mode; - gchar *sync_mode = g_strdup_printf("%d", ext_settings->sync_mode); + gchar* sync_mode = g_strdup_printf("%d", ext_settings->sync_mode); res = uca_phantom_communicate_set_variable(self, UNIT_CAM_SYNCIMG, sync_mode, &sub_error); g_free(sync_mode); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set sync mode to %d:\n\t> %s\n", ext_settings->sync_mode, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set sync mode to %d:\n\t> %s\n", ext_settings->sync_mode, sub_error->message); g_propagate_error(error_loc, phantom_error); g_error_free(sub_error); return FALSE; @@ -1466,11 +1508,13 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu if (ext_settings->frames_per_second != self->settings.frames_per_second || first_call) { self->settings.frames_per_second = ext_settings->frames_per_second; - gchar *fps = g_strdup_printf("%f", ext_settings->frames_per_second); + gchar* fps = g_strdup_printf("%f", ext_settings->frames_per_second); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_RATE, fps, &sub_error); g_free(fps); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set frames per second to %f:\n\t> %s\n", ext_settings->frames_per_second, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set frames per second to %f:\n\t> %s\n", ext_settings->frames_per_second, + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1478,11 +1522,13 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->exposure_time != self->settings.exposure_time || first_call) { self->settings.exposure_time = ext_settings->exposure_time; - gchar *exposure = g_strdup_printf("%f", ext_settings->exposure_time); + gchar* exposure = g_strdup_printf("%f", ext_settings->exposure_time); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_EXP, exposure, &sub_error); g_free(exposure); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set exposure time to %f:\n\t> %s\n", ext_settings->exposure_time, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set exposure time to %f:\n\t> %s\n", ext_settings->exposure_time, + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1490,11 +1536,13 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->roi_pixel_x != self->settings.roi_pixel_x || first_call) { self->settings.roi_pixel_x = ext_settings->roi_pixel_x; - gchar *roi = g_strdup_printf("%d", ext_settings->roi_pixel_x); + gchar* roi = g_strdup_printf("%d", ext_settings->roi_pixel_x); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_META_OX, roi, &sub_error); g_free(roi); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set ROI to %dx%d:\n\t> %s\n", ext_settings->roi_pixel_x, ext_settings->roi_pixel_y, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set ROI to %dx%d:\n\t> %s\n", ext_settings->roi_pixel_x, ext_settings->roi_pixel_y, + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1502,11 +1550,13 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->roi_pixel_y != self->settings.roi_pixel_y || first_call) { self->settings.roi_pixel_y = ext_settings->roi_pixel_y; - gchar *roi = g_strdup_printf("%d", ext_settings->roi_pixel_y); + gchar* roi = g_strdup_printf("%d", ext_settings->roi_pixel_y); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_META_OY, roi, &sub_error); g_free(roi); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set ROI to %dx%d:\n\t> %s\n", ext_settings->roi_pixel_x, ext_settings->roi_pixel_y, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set ROI to %dx%d:\n\t> %s\n", ext_settings->roi_pixel_x, ext_settings->roi_pixel_y, + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1515,37 +1565,43 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu if ((ext_settings->roi_pixel_width != self->settings.roi_pixel_width || ext_settings->roi_pixel_height != self->settings.roi_pixel_height) || first_call) { self->settings.roi_pixel_height = ext_settings->roi_pixel_height; self->settings.roi_pixel_width = ext_settings->roi_pixel_width; - gchar *resolution = g_strdup_printf("%dx%d", ext_settings->roi_pixel_width, ext_settings->roi_pixel_height); - g_print ("Setting resolution to %s\n", resolution); + gchar* resolution = g_strdup_printf("%dx%d", ext_settings->roi_pixel_width, ext_settings->roi_pixel_height); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_RES, resolution, &sub_error); g_free(resolution); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set sensor resolution %dx%d:\n\t> %s\n", ext_settings->sensor_pixel_width, ext_settings->sensor_pixel_height, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set sensor resolution %dx%d:\n\t> %s\n", ext_settings->sensor_pixel_width, + ext_settings->sensor_pixel_height, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } } if (ext_settings->aperture != self->settings.aperture || first_call) { - PhantomReply reply; - self->settings.aperture = ext_settings->aperture; - gchar *aperture = g_strdup_printf("%f", ext_settings->aperture); - res = uca_phantom_communicate_run_command(self, CMD_SET_LENS_APERTURE, &reply, &sub_error, aperture, NULL); - g_free(aperture); - g_free(reply.raw); - if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set aperture to %f:\n\t> %s\n", ext_settings->aperture, sub_error->message); - g_propagate_error(error_loc, phantom_error); - g_clear_error(&sub_error); - } + g_message("Aperture cannot be set by software\n"); // Focal length can only + // be set manually + + // PhantomReply reply; + // self->settings.aperture = ext_settings->aperture; + // gchar *aperture = g_strdup_printf("%f", ext_settings->aperture); + // res = uca_phantom_communicate_run_command(self, CMD_SET_LENS_APERTURE, + // &reply, &sub_error, aperture, NULL); g_free(aperture); g_free(reply.raw); + // if (res != TRUE && sub_error != NULL) { + // g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, + // UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set aperture + // to %f:\n\t> %s\n", ext_settings->aperture, sub_error->message); + // g_propagate_error(error_loc, phantom_error); + // g_clear_error(&sub_error); + // } } if (ext_settings->edr_exp != self->settings.edr_exp || first_call) { self->settings.edr_exp = ext_settings->edr_exp; - gchar *edr_exp = g_strdup_printf("%d", ext_settings->edr_exp); + gchar* edr_exp = g_strdup_printf("%d", ext_settings->edr_exp); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_EDREXP, edr_exp, &sub_error); g_free(edr_exp); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set EDR exposure to %d:\n\t> %s\n", ext_settings->edr_exp, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set EDR exposure to %d:\n\t> %s\n", ext_settings->edr_exp, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1553,11 +1609,12 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->shutter_off != self->settings.shutter_off || first_call) { self->settings.shutter_off = ext_settings->shutter_off; - gchar *shutter_off = g_strdup_printf("%d", ext_settings->shutter_off); + gchar* shutter_off = g_strdup_printf("%d", ext_settings->shutter_off); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_SHOFF, shutter_off, &sub_error); g_free(shutter_off); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set shutter off to %d:\n\t> %s\n", ext_settings->shutter_off, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set shutter off to %d:\n\t> %s\n", ext_settings->shutter_off, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1565,11 +1622,13 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->aexpmode != self->settings.aexpmode || first_call) { self->settings.aexpmode = ext_settings->aexpmode; - gchar *aexpmode = g_strdup_printf("%d", ext_settings->aexpmode); + gchar* aexpmode = g_strdup_printf("%d", ext_settings->aexpmode); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_AEXPMODE, aexpmode, &sub_error); g_free(aexpmode); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set auto exposure mode to %d:\n\t> %s\n", ext_settings->aexpmode, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set auto exposure mode to %d:\n\t> %s\n", ext_settings->aexpmode, + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1577,11 +1636,13 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->aexpcomp != self->settings.aexpcomp || first_call) { self->settings.aexpcomp = ext_settings->aexpcomp; - gchar *aexpcomp = g_strdup_printf("%f", ext_settings->aexpcomp); + gchar* aexpcomp = g_strdup_printf("%f", ext_settings->aexpcomp); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_AEXPCOMP, aexpcomp, &sub_error); g_free(aexpcomp); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set auto exposure compensation to %f:\n\t> %s\n", ext_settings->aexpcomp, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set auto exposure compensation to %f:\n\t> %s\n", ext_settings->aexpcomp, + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1589,62 +1650,58 @@ gboolean uca_phantom_communicate_set_settings(UcaPhantomCommunicate *self, Captu } if (ext_settings->nb_post_trigger_frames != self->settings.nb_post_trigger_frames || first_call) { self->settings.nb_post_trigger_frames = ext_settings->nb_post_trigger_frames; - gchar *nb_post_trigger_frames = g_strdup_printf("%d", ext_settings->nb_post_trigger_frames); + gchar* nb_post_trigger_frames = g_strdup_printf("%d", ext_settings->nb_post_trigger_frames); res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_PTFRAMES, nb_post_trigger_frames, &sub_error); g_free(nb_post_trigger_frames); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, "Failed to set number of post trigger frames to %d:\n\t> %s\n", ext_settings->nb_post_trigger_frames, sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, + "Failed to set number of post trigger frames to %d:\n\t> %s\n", + ext_settings->nb_post_trigger_frames, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } } - g_print ("Settings updated\n"); + g_debug("Settings updated\n"); first_call = FALSE; return TRUE; } -gboolean uca_phantom_communicate_connect_datastream(UcaPhantomCommunicate *self, GError **error_loc) { +gboolean uca_phantom_communicate_connect_datastream(UcaPhantomCommunicate* self, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + g_warning("uca_phantom_communicate_connect_datastream: connecting!\n"); - if (!g_socket_listener_add_inet_port( - G_SOCKET_LISTENER(self->service), self->data_port, NULL, &sub_error)) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_DATASTREAM, "Failed to listen on port %d:\n\t> %s\n", self->data_port, sub_error->message); + GError* sub_error = NULL; + GError* phantom_error = NULL; + + if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(self->service), self->data_port, NULL, &sub_error)) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_DATASTREAM, + "Failed to listen on port %d:\n\t> %s\n", self->data_port, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } // Send the request to connect to the datastream - PhantomReply reply; - - // 7117 - gchar *arg = g_strdup_printf("{port:%d}", self->data_port); + gchar* arg = g_strdup_printf("{port:%d}", self->data_port); // 7117 + gboolean res = uca_phantom_communicate_run_command(self, CMD_START_DATA_CONNECTION, arg, NULL, &sub_error); - gboolean res = uca_phantom_communicate_run_command(self, CMD_START_DATA_CONNECTION, &reply, &sub_error, arg, NULL); - - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_DATASTREAM, "Failed to connect to datastream on port %d:\n\t> %s\n", self->data_port, sub_error->message); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_DATASTREAM, + "Failed to connect to datastream on port %d:\n\t> %s\n", self->data_port, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } - g_free(arg); - arg = NULL; - g_free(reply.raw); - reply.raw = NULL; - self->data_connection = g_socket_listener_accept(G_SOCKET_LISTENER(self->service), NULL, NULL, &sub_error); - if (sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_DATASTREAM, "Failed to connect to datastream on port %d:\n\t> %s\n", self->data_port, sub_error->message); + if (sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_DATASTREAM, + "Failed to connect to datastream on port %d:\n\t> %s\n", self->data_port, sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -1655,19 +1712,20 @@ gboolean uca_phantom_communicate_connect_datastream(UcaPhantomCommunicate *self, self->output_datastream = g_io_stream_get_output_stream(G_IO_STREAM(self->data_connection)); // Get the remote address of the connection - GSocketAddress *remote_address = g_socket_connection_get_remote_address(self->data_connection, NULL); - GSocketAddress *local_address = g_socket_connection_get_local_address(self->data_connection, NULL); + GSocketAddress* remote_address = g_socket_connection_get_remote_address(self->data_connection, NULL); + GSocketAddress* local_address = g_socket_connection_get_local_address(self->data_connection, NULL); guint remote_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(remote_address)); guint local_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(local_address)); - GInetAddress *remote_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(remote_address)); - GInetAddress *local_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(local_address)); + GInetAddress* remote_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(remote_address)); + GInetAddress* local_inet_address = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(local_address)); - gchar *remote_ip_address = g_inet_address_to_string(remote_inet_address); - gchar *local_ip_address = g_inet_address_to_string(local_inet_address); + gchar* remote_ip_address = g_inet_address_to_string(remote_inet_address); + gchar* local_ip_address = g_inet_address_to_string(local_inet_address); - g_message("Data connection established with phantom (%s:%d) -> (%s:%d)\n", local_ip_address, local_port, remote_ip_address, remote_port); + g_message("Data connection established with phantom (%s:%d) -> (%s:%d)\n", local_ip_address, local_port, + remote_ip_address, remote_port); self->data_connection_state = CONNECTED; @@ -1679,16 +1737,11 @@ gboolean uca_phantom_communicate_connect_datastream(UcaPhantomCommunicate *self, return TRUE; } -/** - * uca_phantom_communicate_connect_xdatastream: - * - * Opens a socket that accepts connections from the Phantom on port @port. - * - */ -gboolean uca_phantom_communicate_connect_xdatastream(UcaPhantomCommunicate *self, GError **error_loc) { +gboolean uca_phantom_communicate_connect_xdatastream(UcaPhantomCommunicate* self, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); - GError *phantom_error = NULL; + GError* phantom_error = NULL; char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program fp; @@ -1697,47 +1750,47 @@ gboolean uca_phantom_communicate_connect_xdatastream(UcaPhantomCommunicate *self // Open the device for live capture self->handle = pcap_create(self->xnetcard, errbuf); - if (self->handle == NULL) - { - g_print("Error creating capture self->handle: %s\n", errbuf); + if (self->handle == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, + "Failed to create pcap handle:\n\t> %s\n", errbuf); + g_propagate_error(error_loc, phantom_error); return FALSE; } - printf("Opening device %s for packet capture\n", self->xnetcard); + g_debug("Opening device %s for packet capture\n", self->xnetcard); // Set the capture options pcap_set_snaplen(self->handle, 65535); pcap_set_promisc(self->handle, FALSE); - pcap_set_timeout(self->handle, 1); + pcap_set_timeout(self->handle, 5000); pcap_set_rfmon(self->handle, FALSE); pcap_set_buffer_size(self->handle, MAX_KERNEL_BUF_SIZE); - pcap_set_immediate_mode(self->handle, FALSE); // Set the capture mechanism to PACKET_MMAP + pcap_set_immediate_mode(self->handle, + FALSE); // Set the capture mechanism to PACKET_MMAP // Activate the capture self->handle - if (pcap_activate(self->handle) == -1) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, "Failed to connect to xdatastream:\n\t> %s\n", pcap_geterr(self->handle)); + if (pcap_activate(self->handle) == -1) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, + "Failed to activate pcap handle:\n\t> %s\n", pcap_geterr(self->handle)); g_propagate_error(error_loc, phantom_error); pcap_close(self->handle); self->handle = NULL; return FALSE; } - // ether proto 0x88b7 // Compile the filter to capture packets with ethertype 0x88b7 - if (pcap_compile(self->handle, &fp, "", 1, PCAP_NETMASK_UNKNOWN) == -1) - { - g_print("Error compiling filter: %s\n", pcap_geterr(self->handle)); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, "Failed to connect to xdatastream:\n\t> %s\n", pcap_geterr(self->handle)); + if (pcap_compile(self->handle, &fp, "ether proto 0x88b7", 1, PCAP_NETMASK_UNKNOWN) == -1) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, + "Error compiling filter:\n\t> %s\n", pcap_geterr(self->handle)); g_propagate_error(error_loc, phantom_error); pcap_close(self->handle); self->handle = NULL; return FALSE; } - if (pcap_setfilter(self->handle, &fp) == -1) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, "Failed to connect to xdatastream:\n\t> %s\n", pcap_geterr(self->handle)); + if (pcap_setfilter(self->handle, &fp) == -1) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_CONNECT_XDATASTREAM, + "Error settings filter on pcap handle:\n\t> %s\n", pcap_geterr(self->handle)); g_propagate_error(error_loc, phantom_error); pcap_close(self->handle); @@ -1751,39 +1804,40 @@ gboolean uca_phantom_communicate_connect_xdatastream(UcaPhantomCommunicate *self return TRUE; } -gboolean uca_phantom_communicate_disconnect_datastream(UcaPhantomCommunicate *self, GError **error_loc) { +gboolean uca_phantom_communicate_disconnect_datastream(UcaPhantomCommunicate* self, GError** error_loc) +{ // First check if started readout g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(G_IS_INPUT_STREAM(self->input_datastream), FALSE); g_return_val_if_fail(G_IS_SOCKET_CONNECTION(self->data_connection), FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; // Data stream is ended when the socket is closed - g_print("Closing input stream\n"); g_input_stream_close(self->input_datastream, NULL, &sub_error); if (sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_DISCONNECT_DATASTREAM, "Failed to close input stream:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_DISCONNECT_DATASTREAM, + "Failed to close input stream:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } - g_print("Closing listener\n"); self->data_connection_state = DISCONNECTED; return TRUE; } -gboolean uca_phantom_communicate_disconnect_xdatastream(UcaPhantomCommunicate *self, GError **error_loc) { +gboolean uca_phantom_communicate_disconnect_xdatastream(UcaPhantomCommunicate* self, GError** error_loc) +{ // First check if started readout g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(G_IS_INPUT_STREAM(self->input_datastream), FALSE); g_return_val_if_fail(G_IS_SOCKET_CONNECTION(self->data_connection), FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; pcap_close(self->handle); self->handle = NULL; @@ -1793,31 +1847,24 @@ gboolean uca_phantom_communicate_disconnect_xdatastream(UcaPhantomCommunicate *s return TRUE; } -/** - * @brief - * - * @param self - * @param settings - * @param error_loc - * @return gboolean - */ -gboolean uca_phantom_communicate_arm(UcaPhantomCommunicate *self, guint cine, GError **error_loc) { +gboolean uca_phantom_communicate_arm(UcaPhantomCommunicate* self, guint cine, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; - gchar *cine_str; + GError* sub_error = NULL; + GError* phantom_error = NULL; + gchar* cine_str; gboolean res; PhantomReply reply; // Set the capture settings cine_str = g_strdup_printf("%d", cine); - res = uca_phantom_communicate_run_command(self, CMD_START_RECORDING_IN_A_CINE, &reply, &sub_error, cine_str, NULL); + res = uca_phantom_communicate_run_command(self, CMD_START_RECORDING_IN_A_CINE, cine_str, &reply, &sub_error); g_free(cine_str); - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, "Failed to arm:\n\t> %s\n", sub_error->message); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, + "Failed to arm:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); g_free(reply.raw); @@ -1830,90 +1877,33 @@ gboolean uca_phantom_communicate_arm(UcaPhantomCommunicate *self, guint cine, GE return TRUE; } -/** - * @brief Trigger the camera to save ptframes into a cine in the camera's RAM. - * The cine is set using the rec command. - * - * @param self - * @param pt_frames - * @param error_loc - * @return gboolean - */ -gboolean uca_phantom_communicate_trigger(UcaPhantomCommunicate *self, GError **error_loc) { - g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); - g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - - GError *sub_error = NULL; - GError *phantom_error = NULL; - - gboolean res; - PhantomReply reply; - - res = uca_phantom_communicate_run_command(self, CMD_SOFTWARE_TRIGGER, &reply, &sub_error, NULL); - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, "Failed to trigger:\n\t> %s\n", sub_error->message); - g_propagate_error(error_loc, phantom_error); - g_clear_error(&sub_error); - return FALSE; - } - - g_free(reply.raw); - - return TRUE; -} - -/** - * @brief Trigger the camera to save ptframes into a cine in the camera's RAM. - * The cine is set using the rec command. - * - * @param self - * @param pt_frames - * @param error_loc - * @return gboolean - * - * NOTE: the function first sets the ptframes variable to the desired value, then triggers the camera. - * Therefore there is a slight delay between the function call and the actual trigger. - */ -gboolean uca_phantom_communicate_trigger_ptframes(UcaPhantomCommunicate *self, guint ptframes, GError **error_loc) { +gboolean uca_phantom_communicate_trigger(UcaPhantomCommunicate* self, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; gboolean res; - PhantomReply reply; - gchar *value = g_strdup_printf("%d", ptframes); - - // First set the number of post trigger frames - res = uca_phantom_communicate_set_variable(self, UNIT_DEFC_PTFRAMES, value, &sub_error); - g_free(value); - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_TRIGGER, "Failed to set ptframes:\n\t> %s\n", sub_error->message); - g_propagate_error(error_loc, phantom_error); - g_clear_error(&sub_error); - return FALSE; - } - res = uca_phantom_communicate_run_command(self, CMD_SOFTWARE_TRIGGER, &reply, &sub_error, NULL); - if (res != TRUE && sub_error != NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_TRIGGER, "Failed to trigger:\n\t> %s\n", sub_error->message); + res = uca_phantom_communicate_run_command(self, CMD_SOFTWARE_TRIGGER, NULL, NULL, &sub_error); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, + "Failed to trigger:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } - g_free(reply.raw); - return TRUE; } -// static gboolean uca_phantom_communicate_notify (UcaPhantomCommunicate *self, GError **error_loc) { +// static gboolean uca_phantom_communicate_notify (UcaPhantomCommunicate *self, +// GError **error_loc) { // g_return_val_if_fail (error_loc == NULL || *error_loc == NULL, FALSE); -// g_return_val_if_fail (self->control_connection_state == CONNECTED, FALSE); +// g_return_val_if_fail (self->control_connection_state == CONNECTED, +// FALSE); // GError *sub_error = NULL; // GError *phantom_error = NULL; @@ -1922,11 +1912,13 @@ gboolean uca_phantom_communicate_trigger_ptframes(UcaPhantomCommunicate *self, g // PhantomReply reply; // // Send the notify command -// result = uca_phantom_communicate_run_command (self, CMD_ENABLE_STATUS_CHANGE_NOTIFICATIONS, &reply, &sub_error, "0", NULL); +// result = uca_phantom_communicate_run_command (self, +// CMD_ENABLE_STATUS_CHANGE_NOTIFICATIONS, &reply, &sub_error, "0", NULL); // if (!result) { -// g_set_error (&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_NOTIFY, "Failed to send notify command:\n\t> %s\n", sub_error->message); -// g_propagate_error (error_loc, phantom_error); -// g_clear_error (&sub_error); +// g_set_error (&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, +// UCA_PHANTOM_COMMUNICATE_ERROR_NOTIFY, "Failed to send notify +// command:\n\t> %s\n", sub_error->message); g_propagate_error +// (error_loc, phantom_error); g_clear_error (&sub_error); // g_free(reply.raw); // return FALSE; @@ -1939,11 +1931,12 @@ gboolean uca_phantom_communicate_trigger_ptframes(UcaPhantomCommunicate *self, g // return TRUE; // } -gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate *self, GError **error_loc) { +static gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate* self, GError** error_loc) +{ g_return_val_if_fail(UCA_IS_PHANTOM_COMMUNICATE(self), FALSE); g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); - GError *phantom_error = NULL; + GError* phantom_error = NULL; // Get the MAC address on Linux platform #ifdef __linux__ @@ -1953,9 +1946,9 @@ gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate *self, GE // Open a socket for the ioctl call int fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, "Failed to open socket"); + if (fd < 0) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, + "Failed to open socket"); g_propagate_error(error_loc, phantom_error); return FALSE; } @@ -1964,9 +1957,9 @@ gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate *self, GE g_strlcpy(ifr.ifr_name, self->xnetcard, IFNAMSIZ); // Get the MAC address - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, "Failed to get MAC address"); + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, + "Failed to get MAC address"); g_propagate_error(error_loc, phantom_error); close(fd); return FALSE; @@ -1985,15 +1978,6 @@ gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate *self, GE close(fd); #endif - // print the mac address - g_print("MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", - self->mac_address[0], - self->mac_address[1], - self->mac_address[2], - self->mac_address[3], - self->mac_address[4], - self->mac_address[5]); - // // Get the MAC address on Windows platform // #elif _WIN32 // // Get the MAC address @@ -2001,9 +1985,10 @@ gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate *self, GE // DWORD dwBufLen = sizeof(AdapterInfo); // DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); // if (dwStatus != ERROR_SUCCESS) { - // g_set_error (&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, "Failed to get MAC address"); - // g_propagate_error (error_loc, phantom_error); - // return FALSE; + // g_set_error (&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, + // UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, "Failed to get MAC + // address"); g_propagate_error (error_loc, phantom_error); return + // FALSE; // } // // Find the MAC address of the interface @@ -2017,25 +2002,121 @@ gboolean uca_phantom_communicate_get_mac_address(UcaPhantomCommunicate *self, GE // // Check if the interface was found // if (pAdapterInfo == NULL) { - // g_set_error (&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, "Failed to find interface"); - // g_propagate_error (error_loc, phantom_error); - // return FALSE; + // g_set_error (&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, + // UCA_PHANTOM_COMMUNICATE_ERROR_GET_MAC_ADDRESS, "Failed to find + // interface"); g_propagate_error (error_loc, phantom_error); return + // FALSE; // } return TRUE; } -gboolean uca_phantom_communicate_request_images( - UcaPhantomCommunicate *self, - CaptureSettings *settings, - GError **error_loc) { +gpointer uca_phantom_communicate_request_images_buffered (gpointer data) { + UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE(data); + GError *error = NULL; + GError *sub_error = NULL; + + gchar *request_format = NULL, *request = NULL; + + gint cine = -1, start = 0; // start frame is ignored for cine -1 + guint img_format = settings->image_format, count = MAX_NB_IMAGES_BUFFERING; + + // Make sure we have the mac address, should already be done though + if (self->xenabled && self->mac_address_str == NULL) { + gboolean res = uca_phantom_communicate_get_mac_address(self, &sub_error); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to get MAC address:\n\t> %s\n", sub_error->message); + g_propagate_error(error_loc, phantom_error); + g_clear_error(&sub_error); + return FALSE; + } + + self->mac_address_str = g_strdup_printf("%02x%02x%02x%02x%02x%02x", self->mac_address[0], self->mac_address[1], + self->mac_address[2], self->mac_address[3], self->mac_address[4], self->mac_address[5]); + + if (self->mac_address_str == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for MAC address"); + g_propagate_error(error_loc, phantom_error); + return FALSE; + } + } + + guint command_flag = 0; + + if (self->xenabled) { + + additional = g_strdup_printf(", dest:%s, from:%d", self->mac_address_str, 0); + request_format = g_strdup_printf ( + self->request_image_string, cine, start, count, ImageFormatSpecs[img_format].format_string, additional); + + if (request_format == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for get_ximages command"); + g_propagate_error(error_loc, phantom_error); + g_free(additional); + return FALSE; + } + + command_flag = CMD_GET_XIMAGES; + } + else { + // Setup the arguments for image transfer on 1Gb ethernet + additional = g_strdup_printf("\n"); + request_format = g_strdup_printf(self->request_image_string, cine, start, count, + ImageFormatSpecs[img_format].format_string, additional); + if (request_format == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for get_ximages command"); + g_propagate_error(error_loc, phantom_error); + g_free(additional); + return FALSE; + } + command_flag = CMD_GET_IMAGES; + } + + while (TRUE) { + // Request the datatransfer + gboolean res = uca_phantom_communicate_run_command(self, command_flag, request_format, NULL, &sub_error); + g_free(request_format); + g_free(additional); + + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to request images:\n\t> %s\n", sub_error->message); + g_propagate_error(error_loc, phantom_error); + g_clear_error(&sub_error); + g_free(additional); + return FALSE; + } + + // Create internal request + ImageRequest* request = g_new0(ImageRequest, 1); + request->start = start; + request->nb_images = count; + request->img_format = img_format; + request->end_request = FALSE; + request->buffer_index = current_buffer_index; + + // Increment buffer index + current_buffer_index = current_buffer_index++; + + // Push request to request queue + g_async_queue_push(self->request_queue, request); + } +} + +gboolean uca_phantom_communicate_request_images(UcaPhantomCommunicate* self, CaptureSettings* settings, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); g_return_val_if_fail(self->local_acquisition_state == ACQUIRING, FALSE); - GError *phantom_error = NULL; - GError *sub_error = NULL; - gchar *request_format = NULL; + GError* phantom_error = NULL; + GError* sub_error = NULL; + gchar* request_format = NULL; GValue val = G_VALUE_INIT; GValue val2 = G_VALUE_INIT; gboolean result = FALSE; @@ -2045,17 +2126,21 @@ gboolean uca_phantom_communicate_request_images( gint nb_images = settings->nb_post_trigger_frames + settings->nb_pre_trigger_frames; guint img_format = settings->image_format; + + if (cine >= 0) { // Get first image index from phantom and nb images result = uca_phantom_communicate_get_variable(self, UNIT_CT_FIRSTFR, &val, &phantom_error); if (result != TRUE && phantom_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to get first image index:\n\t> %s\n", phantom_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to get first image index:\n\t> %s\n", phantom_error->message); g_propagate_error(error_loc, phantom_error); return FALSE; } result = uca_phantom_communicate_get_variable(self, UNIT_CT_FRCOUNT, &val2, &phantom_error); if (result != TRUE && phantom_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to get first image index:\n\t> %s\n", phantom_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to get first image index:\n\t> %s\n", phantom_error->message); g_propagate_error(error_loc, phantom_error); return FALSE; } @@ -2069,151 +2154,154 @@ gboolean uca_phantom_communicate_request_images( gint start = 0, total = 0; if (cine_start_index < 0) { - if (-settings->nb_pre_trigger_frames < cine_start_index) { - start = cine_start_index; - } - else { + if (settings->nb_pre_trigger_frames < -cine_start_index) { start = -settings->nb_pre_trigger_frames; + g_print("first -> start: %d, pre_trigger_frames: %d\n", start, -settings->nb_pre_trigger_frames); + } else { + start = cine_start_index; + g_print("second -> start: %d, pre_trigger_frames: %d\n", start, -settings->nb_pre_trigger_frames); } - } - else { + } else { start = cine_start_index; + g_print("third -> start: %d\n", start); } if (cine == -1) { total = 1; - } - else if (nb_images > cine_nb_recorded_images) { + } else if (nb_images > cine_nb_recorded_images) { total = cine_nb_recorded_images; - } - else { + } else { total = nb_images; } g_print("cine_start_index: %d\n", cine_start_index); + g_print("start: %d\n", start); + g_print("settings->nb_pre_trigger_frames: %d\n", settings->nb_pre_trigger_frames); g_print("cine_nb_recorded_images: %d\n", cine_nb_recorded_images); - // Make sure we have the mac address, should already be done though if (self->xenabled && self->mac_address_str == NULL) { gboolean res = uca_phantom_communicate_get_mac_address(self, &sub_error); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to get MAC address:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to get MAC address:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } - - self->mac_address_str = g_strdup_printf ("%02x%02x%02x%02x%02x%02x", - self->mac_address[0], - self->mac_address[1], - self->mac_address[2], - self->mac_address[3], - self->mac_address[4], - self->mac_address[5]); - if (self->mac_address_str) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to allocate memory for MAC address"); + + self->mac_address_str = g_strdup_printf("%02x%02x%02x%02x%02x%02x", self->mac_address[0], self->mac_address[1], + self->mac_address[2], self->mac_address[3], self->mac_address[4], self->mac_address[5]); + + if (self->mac_address_str == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for MAC address"); g_propagate_error(error_loc, phantom_error); return FALSE; } } - // Ask images by blocks of MaxNumberImagesPerRequest (if nb_images > MaxNumberImagesPerRequest) - // so as to not overflow the buffer, and give time to libuca to pull the images incrementally - // Sort of cheated version of a real buffered image request. This method will use a lot of memory - // if the images are not pulled fast enough by libuca. - // Will do fine for now. + // Ask images by blocks of MaxNumberImagesPerRequest (if nb_images > + // MaxNumberImagesPerRequest) so as to not overflow the buffer, and give time + // to libuca to pull the images incrementally Sort of cheated version of a + // real buffered image request. This method will use a lot of memory if the + // images are not pulled fast enough by libuca. Will do fine for now. // TODO: finish implementing a thread safe ring buffer + guint current_buffer_index = 0; + if (self->xenabled) { - gchar *additional = g_strdup_printf (", dest:%s, from:%d\n", self->mac_address_str, 0); + gchar* additional = g_strdup_printf(", dest:%s, from:%d", self->mac_address_str, 0); guint nb_sub_images = 0, nb_images_left = total; while (nb_images_left > 0) { - // format: {cine:, start:, cnt:, fmt:, dest:, from:} - nb_sub_images = nb_images_left > MaxNumberImagesPerRequest[img_format] ? MaxNumberImagesPerRequest[img_format] : nb_images_left; - request_format = g_strdup_printf(self->request_image_string, cine, start, nb_sub_images, ImageFormatSpecs[img_format].format_string, additional); + // format: {cine:, start:, cnt:, + // fmt:, dest:, from:} + nb_sub_images = nb_images_left > MaxNumberImagesPerRequest[img_format] + ? MaxNumberImagesPerRequest[img_format] + : nb_images_left; + request_format = g_strdup_printf(self->request_image_string, cine, start, nb_sub_images, + ImageFormatSpecs[img_format].format_string, additional); if (request_format == NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to allocate memory for get_ximages command"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for get_ximages command"); g_propagate_error(error_loc, phantom_error); - g_free (additional); + g_free(additional); return FALSE; } - start += nb_sub_images; - nb_images_left -= nb_sub_images; // Request the datatransfer - PhantomReply reply = {0, }; - gboolean res = uca_phantom_communicate_run_command( - self, - CMD_GET_XIMAGES, - &reply, - &sub_error, - request_format, - NULL); + gboolean res = uca_phantom_communicate_run_command(self, CMD_GET_XIMAGES, request_format, NULL, &sub_error); g_free(request_format); request_format = NULL; - g_free(reply.raw); - reply.raw = NULL; if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to request images:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to request images:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); - g_free (additional); + g_free(additional); return FALSE; } // Create internal request - InternalRequest *request = g_new0(InternalRequest, 1); + ImageRequest* request = g_new0(ImageRequest, 1); + request->start = start; request->nb_images = nb_sub_images; request->img_format = img_format; request->end_request = FALSE; + request->buffer_index = current_buffer_index; + + // Increment buffer index + current_buffer_index = current_buffer_index++; + + // Update counters + start += (nb_sub_images); + nb_images_left -= (nb_sub_images); // Push request to request queue g_async_queue_push(self->request_queue, request); } - g_free (additional); - } - else { + g_free(additional); + } else { // Setup the arguments for image transfer on 1Gb ethernet - gchar *additional = g_strdup_printf ("\n"); + gchar* additional = g_strdup_printf("\n"); guint nb_sub_images = 0, nb_images_left = total; while (nb_images_left > 0) { - // format: {cine:, start:, cnt:, fmt:, dest:, from:} - nb_sub_images = nb_images_left > MaxNumberImagesPerRequest[img_format] ? MaxNumberImagesPerRequest[img_format] : nb_images_left; - request_format = g_strdup_printf(self->request_image_string, cine, start, nb_sub_images, ImageFormatSpecs[img_format].format_string, additional); + // format: {cine:, start:, cnt:, + // fmt:, dest:, from:} + nb_sub_images = nb_images_left > MaxNumberImagesPerRequest[img_format] + ? MaxNumberImagesPerRequest[img_format] + : nb_images_left; + request_format = g_strdup_printf(self->request_image_string, cine, start, nb_sub_images, + ImageFormatSpecs[img_format].format_string, additional); if (request_format == NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to allocate memory for get_ximages command"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for get_ximages command"); g_propagate_error(error_loc, phantom_error); - g_free (additional); + g_free(additional); return FALSE; } start += nb_sub_images; nb_images_left -= nb_sub_images; // Request the datatransfer - PhantomReply reply = {0, }; - gboolean res = uca_phantom_communicate_run_command( - self, - CMD_GET_IMAGES, - &reply, - &sub_error, - request_format, - NULL); + PhantomReply reply = { + 0, + }; + gboolean res = uca_phantom_communicate_run_command(self, CMD_GET_IMAGES, request_format, NULL, &sub_error); g_free(request_format); request_format = NULL; - g_free(reply.raw); - reply.raw = NULL; if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, "Failed to request images:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to request images:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); - g_free (additional); + g_free(additional); return FALSE; } // Create internal request - InternalRequest *request = g_new0(InternalRequest, 1); + ImageRequest* request = g_new0(ImageRequest, 1); request->nb_images = nb_sub_images; request->img_format = img_format; request->end_request = FALSE; @@ -2221,122 +2309,40 @@ gboolean uca_phantom_communicate_request_images( // Push request to request queue g_async_queue_push(self->request_queue, request); } - g_free (additional); - } + g_free(additional); + } + + if (self->timestamping || settings->timestamp_format != TS_NONE) { + // Request the timestamps: + // format: time {cine:, start:, + // cnt:[, from:]} + gchar* request_format = g_strdup_printf("{cine:%d, start:%d, cnt:%d, from:%d}", cine, start, total, 0); + if (request_format == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to allocate memory for time command"); + g_propagate_error(error_loc, phantom_error); + return FALSE; + } + gboolean res = uca_phantom_communicate_run_command(self, CMD_GET_TIMESTAMPS, request_format, NULL, &sub_error); + if (res != TRUE && sub_error != NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_REQUEST_IMAGES, + "Failed to request timestamps:\n\t> %s\n", sub_error->message); + g_propagate_error(error_loc, phantom_error); + g_clear_error(&sub_error); + return FALSE; + } + g_free(request_format); + } return TRUE; } -static void uca_phantom_communicate_free_request(InternalRequest *request) { +static void uca_phantom_communicate_free_request(ImageRequest* request) +{ // TODO: check if CaptureSettings needs to be freed g_free(request); } -// /** -// * @brief -// * -// * @param data -// * @return gpointer -// */ -// static gpointer uca_phantom_communicate_accept_timestamps (UcaPhantomCommunicate *self, InternalRequest *request) { -// g_return_val_if_fail (self->control_connection_state == CONNECTED, NULL); - -// GError *sub_error = NULL; -// gsize ts_size = 0; -// gsize ts_packet_size = 0; -// gsize bytes_read = 0; -// gpointer ts_buffer = NULL; -// gboolean head_found = FALSE; -// gboolean tail_found = FALSE; -// gchar notify_tail = 1; -// gchar event[128] = {0, }; -// guint counter = 0; -// gboolean read_all = FALSE; - -// g_print ("Start accepting timestamps\n"); - -// PhantomReply reply; - -// // First wait for the event notification that the cine is stored -// for (; !head_found && !tail_found && counter < 128; counter++) { -// g_print ("Event: %c\n", event[counter]); - -// bytes_read = g_input_stream_read (self->input_datastream, event+counter, 1, NULL, &sub_error); -// if (sub_error != NULL || bytes_read != 1) { -// g_warning ("Failed to read event notification:\n\t> %s\n", sub_error->message); -// g_clear_error (&sub_error); -// return NULL; -// } - -// g_print ("Event: %c\n", event[counter]); - -// if (event[counter] == '@' && !head_found) { -// head_found = TRUE; -// } -// else if (event[counter] == '@' && !tail_found) { -// tail_found = TRUE; -// event[counter+1] = '\0'; -// } -// } - -// if (head_found && tail_found) { -// g_print ("Event notification received: %s\n", event); -// } -// else { -// g_warning ("Failed to receive event notification\n"); -// return NULL; -// } - -// // Setup the arguments for get_timestamps command -// gchar* ts_args = g_strdup_printf ("{cine:%d, start:%d, cnt:%d}", 1, 0, 1); -// gboolean res = uca_phantom_communicate_run_command ( -// self, -// CMD_GET_TIMESTAMPS, -// &reply, -// &sub_error, -// ts_args, -// NULL); -// if (res != TRUE && sub_error != NULL) { -// g_warning ("Failed to get timestamping:\n\t> %s\n", sub_error->message); -// g_free (ts_args); -// g_free (reply.raw); -// g_clear_error (&sub_error); -// return NULL; -// } -// g_free (ts_args); -// ts_args = NULL; -// g_free (reply.raw); -// reply.raw = NULL; - -// // Allocate the timestamp buffer -// ts_size = TimestampSize[self->ts_format]; -// ts_packet_size = ts_size * request->nb_images; -// ts_buffer = g_malloc0 (ts_packet_size); -// if (ts_buffer == NULL) { -// g_print ("Failed to allocate timestamp buffer\n"); -// return NULL; -// } - -// // Read the timestamp from the data stream -// read_all = g_input_stream_read_all (self->input_datastream, ts_buffer, ts_packet_size, &bytes_read, NULL, &sub_error); -// if (read_all != TRUE && sub_error != NULL) { -// g_print("Failed to read datastream:\n\t> %s\n", sub_error->message); -// g_clear_error (&sub_error); -// return NULL; -// } - -// if (bytes_read != ts_size) { -// g_print ("Failed to read all bytes of timestamp frame from datastream. Read %ld bytes, expected %ld bytes. Buffer will be padded.\n", bytes_read, ts_size); -// return NULL; -// } -// else { -// g_print ("Read %ld bytes of timestamp frame from datastream.\n", bytes_read); -// } - -// // return the timestamp buffer -// return ts_buffer; -// } - /** * Read images from 1Gb ethernet connection * @@ -2347,13 +2353,14 @@ static void uca_phantom_communicate_free_request(InternalRequest *request) { * * @note This function uses a TCP connection to read images from the camera. */ -static gpointer uca_phantom_communicate_accept_img(gpointer data) { - UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE(data); +static gpointer uca_phantom_communicate_accept_img(gpointer data) +{ + UcaPhantomCommunicate* self = UCA_PHANTOM_COMMUNICATE(data); g_return_val_if_fail(self->control_connection_state == CONNECTED, NULL); - GError *error = NULL; - GError *sub_error = NULL; + GError* error = NULL; + GError* sub_error = NULL; gsize image_size = 0; gsize ts_size = 0; gsize image_packet_size = 0; @@ -2364,30 +2371,22 @@ static gpointer uca_phantom_communicate_accept_img(gpointer data) { gboolean read_all = FALSE; guint nb_pixels = 0; - g_print("Start accepting images\n"); - self->input_datastream = g_io_stream_get_input_stream(G_IO_STREAM(self->data_connection)); - InternalRequest *request = NULL; - - g_print("Start loop in thread\n"); + ImageRequest* request = NULL; - while (TRUE) - { + while (TRUE) { bytes_read = 0; // Wait for a request to be available request = g_async_queue_pop(self->request_queue); - g_print("Popped request from queue!\n"); - - if (request == NULL) - { + if (request == NULL) { g_print("Failed to pop request from queue\n"); - g_error_new(UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_ACCEPT_IMG, "Failed to pop request from queue: request is NULL\n"); + g_error_new(UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_ACCEPT_IMG, + "Failed to pop request from queue: request is NULL\n"); return error; } - if (request->end_request == TRUE) - { + if (request->end_request == TRUE) { uca_phantom_communicate_free_request(request); break; } @@ -2404,51 +2403,42 @@ static gpointer uca_phantom_communicate_accept_img(gpointer data) { // Allocate the image buffer image_buffer = g_malloc0(image_packet_size); - if (image_buffer == NULL) - { - error = g_error_new_literal(UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_ACCEPT_IMG, "Failed to allocate image buffer\n"); + if (image_buffer == NULL) { + error = g_error_new_literal(UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_ACCEPT_IMG, + "Failed to allocate image buffer\n"); return error; } // Read the image data from the data stream - read_all = g_input_stream_read_all(self->input_datastream, image_buffer, image_packet_size, &bytes_read, NULL, &sub_error); - if (read_all != TRUE && sub_error != NULL) - { - error = g_error_new(UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_ACCEPT_IMG, "Failed to read datastream:\n\t> %s\n", sub_error->message); + read_all = g_input_stream_read_all(self->input_datastream, image_buffer, image_packet_size, &bytes_read, NULL, + &sub_error); + if (read_all != TRUE && sub_error != NULL) { + error = g_error_new(UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_ACCEPT_IMG, + "Failed to read datastream:\n\t> %s\n", sub_error->message); g_clear_error(&sub_error); g_free(image_buffer); return error; - } - else if (bytes_read != image_packet_size) - { - g_print("Failed to read all bytes of image frame from datastream. Read %ld bytes, expected %ld bytes. Buffer will be padded.\n", bytes_read, image_packet_size); + } else if (bytes_read != image_packet_size) { + g_print("Failed to read all bytes of image frame from datastream. Read %ld " + "bytes, expected %ld bytes. Buffer will be padded.\n", + bytes_read, image_packet_size); } g_print("Read %ld bytes from datastream\n", bytes_read); - // If self->timestamping is enabled, read the timestamp from the data stream - // if (self->timestamping) { - // ts_buffer = uca_phantom_communicate_accept_timestamps (self, request); - - // if (ts_buffer == NULL) { - // g_print ("Failed to read timestamps\n"); - // } - // } - // Append to the self->unpacked_images array g_ptr_array_add(self->unpacked_images, image_buffer); // Append a copy of the current capture settings - CaptureSettings *csettings = g_memdup(&self->settings, sizeof(CaptureSettings)); + CaptureSettings* csettings = g_memdup(&self->settings, sizeof(CaptureSettings)); g_ptr_array_add(self->capture_settings, csettings); gpointer output_buffer = image_buffer; // Create a CineData struct for each image in the image buffer - for (guint i = 0; i < request->nb_images; i++) - { + for (guint i = 0; i < request->nb_images; i++) { // Create a new CineData struct - CineData *cine_data = g_new0(CineData, 1); + CineData* cine_data = g_new0(CineData, 1); cine_data->Settings = csettings; cine_data->NbImages = 1; @@ -2458,7 +2448,6 @@ static gpointer uca_phantom_communicate_accept_img(gpointer data) { cine_data->RawImages = NULL; cine_data->UnpackedImages = output_buffer; - cine_data->RawTimestamps = ts_buffer; // Increment the output buffer pointer output_buffer += nb_pixels; @@ -2471,8 +2460,6 @@ static gpointer uca_phantom_communicate_accept_img(gpointer data) { uca_phantom_communicate_free_request(request); } - g_print("Exiting image accept thread\n"); - return NULL; } @@ -2482,8 +2469,9 @@ static gpointer uca_phantom_communicate_accept_img(gpointer data) { * @param data * @return gpointer */ -static gpointer uca_phantom_communicate_accept_ximg(gpointer data) { - UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE(data); +static gpointer uca_phantom_communicate_accept_ximg(gpointer data) +{ + UcaPhantomCommunicate* self = UCA_PHANTOM_COMMUNICATE(data); g_return_val_if_fail(self->control_connection_state == CONNECTED, NULL); guint nb_pixels = 0; @@ -2495,17 +2483,15 @@ static gpointer uca_phantom_communicate_accept_ximg(gpointer data) { // gssize ts_size = 0; // gssize ts_packet_size = 0; gssize bytes_read = 0; - guint8 *image_buffer = NULL; - guint8 *buffer_pointer = NULL; - const guint8 *pkt_data = NULL; + guint8* image_buffer = NULL; + guint8* buffer_pointer = NULL; + const guint8* pkt_data = NULL; // gpointer ts_buffer = NULL; int read_all = FALSE; - InternalRequest *request = NULL; - - struct pcap_pkthdr *pkt_header = NULL; + ImageRequest* request = NULL; - g_print("Accept thread: Start accepting images in loop\n"); + struct pcap_pkthdr* pkt_header = NULL; // use g_socket_receive_from in a loop to receive the data from the socket while (TRUE) { @@ -2513,81 +2499,72 @@ static gpointer uca_phantom_communicate_accept_ximg(gpointer data) { // Wait for a request to be available request = g_async_queue_pop(self->request_queue); - g_print("Accept thread: Popped request from queue!\n"); - if (request == NULL) { - g_print("Accept thread: Failed to pop request from queue\n"); return NULL; } if (request->end_request == TRUE) { - g_print("Accept thread: Received end request in uca_phantom_communicate_accept_ximg!\n"); uca_phantom_communicate_free_request(request); // Push the end request to the packed queue - CineData *cine_data = g_new0(CineData, 1); + CineData* cine_data = g_new0(CineData, 1); cine_data->RawImages = NULL; cine_data->UnpackedImages = NULL; - cine_data->RawTimestamps = NULL; // Add the data to the queue g_async_queue_push(self->packed_queue, cine_data); - return NULL; - } + if (self->timestamping) { + // Create timestamp request + TsRequest* ts_request = g_new0(TsRequest, 1); + ts_request->end_request = TRUE; - // print settings.width and settings.height - g_print("Accept thread: Width: %d, Height: %d\n", self->settings.roi_pixel_width, self->settings.roi_pixel_height); + // Push request to request queue + g_async_queue_push(self->ts_request_queue, ts_request); + } + return NULL; + } // Calculate the size of the image buffer nb_pixels = self->settings.roi_pixel_width * self->settings.roi_pixel_height; packed_image_size = nb_pixels * ImageFormatSpecs[request->img_format].byte_depth; // the packed image size - packed_packet_size = packed_image_size * request->nb_images; // the packed image packet size - unpacked_image_size = nb_pixels * sizeof(guint16); // the unpacked image size - unpacked_packet_size = unpacked_image_size * request->nb_images; // the unpacked image packet size - - // print number of images and image size - g_print("Accept thread: Number of images: %ld\n", request->nb_images); + packed_packet_size = packed_image_size * request->nb_images; // the packed image packet size + unpacked_image_size = nb_pixels * sizeof(guint16); // the unpacked image size + unpacked_packet_size = unpacked_image_size * request->nb_images; // the unpacked image packet size buffer_size = packed_packet_size; image_buffer = g_malloc0(buffer_size + 128); - if (image_buffer == NULL) - { - g_print("Failed to allocate image buffer\n"); + + if (image_buffer == NULL) { + g_warning("Out of RAM... \n"); return data; } remaining_bytes = buffer_size; buffer_pointer = image_buffer; - g_print("input packet size: %ld\n", buffer_size); - // Read the image data directly from kernel buffer using pcap_next_ex while (TRUE) { if (remaining_bytes <= 0) { - g_print("Accept thread: All bytes of image frame have been read\n"); break; // All bytes of the image frame have been read } read_all = pcap_next_ex(self->handle, &pkt_header, &pkt_data); if (read_all == 0) { - g_print("Beeing read from live capture\n"); - } - else if (read_all == -1) { - g_print("Error occurred\n"); - gchar *error = pcap_geterr(self->handle); - - g_print("Error: %s\n", error); - } - else if (read_all == -2) { - g_print("Being read from savefile\n"); + g_debug("Beeing read from live capture\n"); + } else if (read_all == -1) { + g_debug("Error occurred\n"); + gchar* error = pcap_geterr(self->handle); + + g_debug("Error: %s\n", error); + } else if (read_all == -2) { + g_debug("Being read from savefile\n"); } // Check if the packet size exceeds the remaining space in the buffer if (pkt_header->len - ETHERNET_HEADER_SIZE > remaining_bytes) { to_read = remaining_bytes; - } - else { + } else { to_read = pkt_header->len - ETHERNET_HEADER_SIZE; } @@ -2601,17 +2578,13 @@ static gpointer uca_phantom_communicate_accept_ximg(gpointer data) { bytes_read = packed_packet_size - remaining_bytes; if (bytes_read != packed_packet_size) { - g_print("Failed to read all bytes of image frame from datastream. Read %ld bytes, expected %ld bytes. Buffer will be padded.\n", bytes_read, packed_packet_size); - } - g_print("Accept thread: Read %ld bytes from datastream\n", bytes_read); - - for (int i = 0; i < 10; i++) { - g_print("%d ", *(guint8 *)(image_buffer + i)); + g_debug("Failed to read all bytes of image frame from datastream. Read %ld " + "bytes, expected %ld bytes. Buffer will be padded.\n", + bytes_read, packed_packet_size); } - g_print("\n"); // Create a new CineData struct - CineData *cine_data = g_new0(CineData, 1); + CineData* cine_data = g_new0(CineData, 1); cine_data->Settings = g_memdup(&self->settings, sizeof(CaptureSettings)); cine_data->ImgFormat = request->img_format; @@ -2622,18 +2595,28 @@ static gpointer uca_phantom_communicate_accept_ximg(gpointer data) { cine_data->RawImages = image_buffer; cine_data->UnpackedImages = NULL; - cine_data->RawTimestamps = NULL; + + cine_data->buffer_index = request->buffer_index; // Add the data to the queue g_async_queue_push(self->packed_queue, cine_data); - g_print("Accept thread: pushed the received image to packed queue \n"); + + if (self->timestamping) { + // Create timestamp request + TsRequest* ts_request = g_new0(TsRequest, 1); + ts_request->start = request->start; + ts_request->count = request->nb_images; + ts_request->end_request = FALSE; + ts_request->TsFormat = self->settings.timestamp_format; + + // Push request to request queue + g_async_queue_push(self->ts_request_queue, ts_request); + } // Free the request uca_phantom_communicate_free_request(request); } - g_print("Exiting image accept thread\n"); - return NULL; } @@ -2644,18 +2627,20 @@ static gpointer uca_phantom_communicate_accept_ximg(gpointer data) { * @param error_loc * @return gboolean */ -gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, CineData *cine_data, GError **error_loc) { +static gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate* self, CineData* cine_data, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(cine_data != NULL, FALSE); - GError *phantom_error = NULL; + GError* phantom_error = NULL; gsize output_size = cine_data->SizePerImageUnpacked * cine_data->NbImages; // Allocate memory for the unpacked image - guint16 *unpacked_image = g_malloc0(output_size); - if (unpacked_image == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, "Failed to allocate memory for unpacked image"); + guint16* unpacked_image = g_malloc0(output_size); + if (unpacked_image == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, + "Failed to allocate memory for unpacked image"); g_propagate_error(error_loc, phantom_error); return FALSE; } @@ -2665,7 +2650,8 @@ gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, C __m128i sm2 = _mm_setr_epi8(0x80, 0x80, 0x80, 0x80, 3, 2, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 8, 7, 0x80, 0x80); __m128i sm3 = _mm_setr_epi8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 4, 3, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 9, 8); - __m128i mask2 = _mm_setr_epi8(0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0, 0, 0, 0, 0, 0); + __m128i mask2 = _mm_setr_epi8(0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, + 0b11111111, 0b11111111, 0b11111111, 0, 0, 0, 0, 0, 0); __m128i m0 = _mm_setr_epi8(0b11000000, 0b11111111, 0, 0, 0, 0, 0, 0, 0b11000000, 0b11111111, 0, 0, 0, 0, 0, 0); __m128i m1 = _mm_setr_epi8(0, 0, 0b11110000, 0b00111111, 0, 0, 0, 0, 0, 0, 0b11110000, 0b00111111, 0, 0, 0, 0); __m128i m2 = _mm_setr_epi8(0, 0, 0, 0, 0b11111100, 0b00001111, 0, 0, 0, 0, 0, 0, 0b11111100, 0b00001111, 0, 0); @@ -2674,18 +2660,18 @@ gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, C guint input_index = 0; guint output_index = 0; - if (cine_data->NbPixelsPerImage % 8 != 0) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, "Image size is not a multiple of 8"); + if (cine_data->NbPixelsPerImage % 8 != 0) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, + "Image size is not a multiple of 8"); g_propagate_error(error_loc, phantom_error); return FALSE; } __m128i input, shifted0, shifted1, shifted2, shifted3, result; - if (cine_data->RawImages == NULL) - { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, "Raw image data is NULL"); + if (cine_data->RawImages == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, + "Raw image data is NULL"); g_propagate_error(error_loc, phantom_error); return FALSE; } @@ -2697,15 +2683,13 @@ gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, C int limit = 0; int i = 0; - while (output_index < cine_data->NbPixelsPerImage * cine_data->NbImages) - { + while (output_index < cine_data->NbPixelsPerImage * cine_data->NbImages) { new_length = packet_size - input_index; usable_length = new_length - (new_length % 10); limit = input_index + usable_length; - for (i = input_index; i < limit; i += 10) - { - input = _mm_loadu_si128((__m128i *)(cine_data->RawImages + input_index)); + for (i = input_index; i < limit; i += 10) { + input = _mm_loadu_si128((__m128i*)(cine_data->RawImages + input_index)); input = _mm_and_si128(input, mask2); @@ -2716,7 +2700,7 @@ gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, C result = _mm_or_si128(_mm_or_si128(shifted0, shifted1), _mm_or_si128(shifted2, shifted3)); - _mm_storeu_si128((__m128i *)(unpacked_image + output_index), result); + _mm_storeu_si128((__m128i*)(unpacked_image + output_index), result); input_index += 10; output_index += 8; @@ -2727,8 +2711,6 @@ gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, C // g_warning("Pixel index is not equal to the number of pixels"); // } - g_print("pixel index: %d\n", output_index); - cine_data->UnpackedImages = unpacked_image; g_free(cine_data->RawImages); @@ -2742,33 +2724,40 @@ gboolean uca_phantom_communicate_unpack_image_p10(UcaPhantomCommunicate *self, C * @param error_loc * @return gboolean */ -gboolean uca_phantom_communicate_unpack_image_p12l(UcaPhantomCommunicate *self, CineData *cine_data, GError **error_loc) { +static gboolean uca_phantom_communicate_unpack_image_p12l(UcaPhantomCommunicate* self, CineData* cine_data, + GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(cine_data != NULL, FALSE); - GError *phantom_error = NULL; + GError* phantom_error = NULL; gsize output_size = cine_data->SizePerImageUnpacked * cine_data->NbImages; // Allocate memory for the unpacked image - guint16 *unpacked_image = g_malloc0(output_size); + guint16* unpacked_image = g_malloc0(output_size); if (unpacked_image == NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, "Failed to allocate memory for unpacked image"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, + "Failed to allocate memory for unpacked image"); g_propagate_error(error_loc, phantom_error); return FALSE; } __m128i sm0 = _mm_setr_epi8(1, 0, 0x80, 0x80, 4, 3, 0x80, 0x80, 7, 6, 0x80, 0x80, 10, 9, 0x80, 0x80); __m128i sm1 = _mm_setr_epi8(0x80, 0x80, 2, 1, 0x80, 0x80, 5, 4, 0x80, 0x80, 8, 7, 0x80, 0x80, 11, 10); - __m128i m0 = _mm_setr_epi8(0b11110000, 0b11111111, 0, 0, 0b11110000, 0b11111111, 0, 0, 0b11110000, 0b11111111, 0, 0, 0b11110000, 0b11111111, 0, 0); - __m128i m1 = _mm_setr_epi8(0, 0, 0b11111111, 0b00001111, 0, 0, 0b11111111, 0b00001111, 0, 0, 0b11111111, 0b00001111, 0, 0, 0b11111111, 0b00001111); - __m128i mask2 = _mm_setr_epi8(0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0, 0, 0, 0); + __m128i m0 = _mm_setr_epi8(0b11110000, 0b11111111, 0, 0, 0b11110000, 0b11111111, 0, 0, 0b11110000, 0b11111111, 0, 0, + 0b11110000, 0b11111111, 0, 0); + __m128i m1 = _mm_setr_epi8(0, 0, 0b11111111, 0b00001111, 0, 0, 0b11111111, 0b00001111, 0, 0, 0b11111111, 0b00001111, + 0, 0, 0b11111111, 0b00001111); + __m128i mask2 = _mm_setr_epi8(0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, + 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0, 0, 0, 0); guint input_index = 0; guint output_index = 0; if (cine_data->NbPixelsPerImage % 8 != 0) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, "Image size is not a multiple of 8"); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, + "Image size is not a multiple of 8"); g_propagate_error(error_loc, phantom_error); return FALSE; } @@ -2777,7 +2766,7 @@ gboolean uca_phantom_communicate_unpack_image_p12l(UcaPhantomCommunicate *self, while (output_index < cine_data->NbPixelsPerImage * cine_data->NbImages) { // Load 8 pixels, i.e. 80 bits = 10 bytes - input = _mm_loadu_si128((__m128i *)(cine_data->RawImages + input_index)); + input = _mm_loadu_si128((__m128i*)(cine_data->RawImages + input_index)); // Mask input = _mm_and_si128(input, mask2); @@ -2790,14 +2779,14 @@ gboolean uca_phantom_communicate_unpack_image_p12l(UcaPhantomCommunicate *self, result = _mm_or_si128(shifted0, shifted1); // Store - _mm_storeu_si128((__m128i *)(unpacked_image + output_index), result); + _mm_storeu_si128((__m128i*)(unpacked_image + output_index), result); output_index += 8; input_index += 12; } if (output_index != cine_data->NbPixelsPerImage * cine_data->NbImages) { - g_warning("Pixel index is not equal to the number of pixels"); + g_warning("Error while unpacking image"); } cine_data->UnpackedImages = unpacked_image; @@ -2815,25 +2804,23 @@ gboolean uca_phantom_communicate_unpack_image_p12l(UcaPhantomCommunicate *self, * TODO: May need to modify the data structure that holds the cine data! * */ -gpointer uca_phantom_communicate_unpack_ximg(gpointer data) { - UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE(data); +static gpointer uca_phantom_communicate_unpack_ximg(gpointer data) +{ + UcaPhantomCommunicate* self = UCA_PHANTOM_COMMUNICATE(data); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; int i = 0; - g_print("Unpacking thread started\n"); - // Loop on CineData objects in the queue while (TRUE) { // Get the next CineData object - CineData *cine_data = g_async_queue_pop(self->packed_queue); + CineData* cine_data = g_async_queue_pop(self->packed_queue); if (cine_data->UnpackedImages == NULL && cine_data->RawImages == NULL) { - g_print("Unpacking thread: exiting because cine_data NULL\n"); - // // Push the CineData object to the queue anyways, to exit the other threads - // g_async_queue_push (self->unpacked_queue, cine_data); + // Push the CineData object to the queue anyways, to exit the other + // threads g_async_queue_push (self->unpacked_queue, cine_data); g_free(cine_data); break; } @@ -2845,17 +2832,15 @@ gpointer uca_phantom_communicate_unpack_ximg(gpointer data) { g_clear_error(&sub_error); return phantom_error; } - } - else if (cine_data->ImgFormat == IMG_P12L) { + } else if (cine_data->ImgFormat == IMG_P12L) { if (!uca_phantom_communicate_unpack_image_p12l(self, cine_data, &sub_error)) { g_propagate_error(&phantom_error, sub_error); g_clear_error(&sub_error); return phantom_error; } - } - else { - g_print("Unpacking thread: Invalid format\n"); - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, "Image format not supported over 10Gb Ethernet."); + } else { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_UNPACK_IMAGE, + "Image format not supported over 10Gb Ethernet."); return phantom_error; } @@ -2865,17 +2850,13 @@ gpointer uca_phantom_communicate_unpack_ximg(gpointer data) { // Append a copy of the current capture settings g_ptr_array_add(self->capture_settings, cine_data->Settings); - for (int i = 0; i < 10; i++) { - g_print("%d ", *(guint16 *)(cine_data->UnpackedImages + i)); - } - // Create a CineData struct for each image in the image buffer // CineData *new_cine_data = g_new0 (CineData, cine_data->nb_images); gpointer image_buffer_offset = cine_data->UnpackedImages; for (guint i = 0; i < cine_data->NbImages; i++) { // Get a pointer to the current CineData struct in the array - CineData *new_cine_data = g_new0(CineData, 1); + CineData* new_cine_data = g_new0(CineData, 1); new_cine_data->Settings = cine_data->Settings; new_cine_data->NbImages = 1; @@ -2885,40 +2866,162 @@ gpointer uca_phantom_communicate_unpack_ximg(gpointer data) { new_cine_data->RawImages = NULL; new_cine_data->UnpackedImages = image_buffer_offset; - new_cine_data->RawTimestamps = NULL; - // Update the offset + new_cine_data->buffer_index = cine_data->buffer_index; + + // Update the offsets image_buffer_offset += cine_data->SizePerImageUnpacked; // Push it to the unpacked queue g_async_queue_push(self->unpacked_queue, new_cine_data); } + // TODO: is this necessary? don't think so + self->buffer_index_counter++; + // Free the cine data g_free(cine_data); - i++; } - g_print("Unpack thread finished\n"); - return NULL; } /** - * @brief + * @brief Accepts timestamp data from the input datastream and pushes it to the + * timestamp queue * - * @param self - * @param error_loc - * @return gboolean + * @details This function is called in a new thread to accept timestamp data + * from the input datastream and push it to the timestamp queue. It loops on + * CineData objects in the queue and receives the timestamp data from the input + * datastream. If an error occurs during the read, it returns a GError object. + * Otherwise, it creates a TimestampData struct and pushes it to the timestamp + * queue. * - * dont hard code the port number + * @param data A pointer to the UcaPhantomCommunicate object + * @return gpointer NULL */ -gboolean uca_phantom_communicate_start_readout(UcaPhantomCommunicate *self, GError **error_loc) { - g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); - g_return_val_if_fail(self->local_acquisition_state == IDLE, FALSE); +static gpointer uca_phantom_communicate_accept_timestamps(gpointer data) +{ + UcaPhantomCommunicate* self = UCA_PHANTOM_COMMUNICATE(data); + + GError* sub_error = NULL; + GError* phantom_error = NULL; + gssize bytes_read = 0; + + // Loop on CineData objects in the queue + while (TRUE) { + // Get the next CineData object + TsRequest* ts_request = g_async_queue_pop(self->ts_request_queue); + + if (ts_request->end_request == TRUE) { + // exit the thread + g_free(ts_request); + break; + } + + gsize packet_size = ts_request->count * TimestampSpecs[ts_request->TsFormat].byte_size; + + g_print("count: %d, size: %ld\n", ts_request->count, TimestampSpecs[ts_request->TsFormat].byte_size); + + g_print("Requesting %ld packet_size\n", packet_size); + + // Allocate memory for the timestamp data + guint8* ts_data = g_malloc0(packet_size); + + // Receive the timestamp data from the self->input_datastream + gsize bytes_received = 0; + while (bytes_received < packet_size) { + bytes_read = g_input_stream_read(self->input_datastream, ts_data + bytes_received, + packet_size - bytes_received, NULL, &sub_error); + if (bytes_read < 0) { + g_propagate_error(&phantom_error, sub_error); + g_clear_error(&sub_error); + g_free(ts_data); + g_free(ts_request); + return phantom_error; + } + bytes_received += bytes_read; + } + + Timestamp* timestamps = g_new0(Timestamp, ts_request->count); + memcpy(timestamps, ts_data, packet_size); + + // Free the timestamp data + g_free(ts_data); + + // Create a TimestampData struct + TimestampData* timestamp_data = g_new0(TimestampData, 1); + timestamp_data->start = ts_request->start; + timestamp_data->count = ts_request->count; + timestamp_data->TsFormat = ts_request->TsFormat; + timestamp_data->timestamps = timestamps; + + // Print the first timestamp + g_print("Timestamp: %u\n", (*timestamps).ts0.csecs); + + // Push it to the unpacked queue + g_async_queue_push(self->ts_queue, timestamp_data); + + // Free the request + g_free(ts_request); + } + + return NULL; +} + +gpointer uca_phantom_communicate_buffer_images (gpointer data) { + UcaPhantomCommunicate *self = UCA_PHANTOM_COMMUNICATE (data); GError *sub_error = NULL; GError *phantom_error = NULL; + + g_print ("Buffering thread started\n"); + + // Loop on CineData objects in the queue + while (TRUE) { + // Get the next CineData object + CineData *cine_data = g_async_queue_pop (self->unpacked_queue); + + if (cine_data->UnpackedImages == NULL && cine_data->RawImages == NULL) { + g_print ("Buffering thread: exiting because cine_data NULL\n"); + // // Push the CineData object to the queue anyways, to exit the other threads + // g_async_queue_push (self->unpacked_queue, cine_data); + g_free (cine_data); + break; + } + + // Write the image data to the ring buffer + gpointer result = ringbuf_memcpy_into (self->ring_buffer, cine_data->UnpackedImages, cine_data->SizePerImageUnpacked); + + // Free the actual image data + // If we arrive in a new buffer, free the previous one + if (cine_data->buffer_index != prev_buffer_index) { + g_debug("Freeing buffer %d\n", prev_buffer_index); + g_free(g_ptr_array_remove_index(self->unpacked_images, prev_buffer_index)); + prev_buffer_index = cine_data->buffer_index; + } + + // Free the cine data + g_free (cine_data); + + if (result == NULL) { + g_print ("Error in the ring buffer...\n"); + continue; + } + } + + g_print ("Buffering thread finished\n"); + + return NULL; +} + +gboolean uca_phantom_communicate_start_readout(UcaPhantomCommunicate* self, GError** error_loc) +{ + g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); + g_return_val_if_fail(self->local_acquisition_state == IDLE, FALSE); + + GError* sub_error = NULL; + GError* phantom_error = NULL; gboolean result = FALSE; gboolean ts_result = FALSE; @@ -2927,88 +3030,120 @@ gboolean uca_phantom_communicate_start_readout(UcaPhantomCommunicate *self, GErr // Get the mac address of the camera gboolean res = uca_phantom_communicate_get_mac_address(self, &sub_error); if (res != TRUE && sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, "Failed to get MAC address:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, + UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, "Failed to get MAC address:\n\t> %s\n", + sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; } - - self->mac_address_str = g_strdup_printf("%02x%02x%02x%02x%02x%02x", - self->mac_address[0], - self->mac_address[1], - self->mac_address[2], - self->mac_address[3], - self->mac_address[4], - self->mac_address[5]); - if (self->mac_address_str) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, "Failed to allocate memory for MAC address"); + + self->mac_address_str = g_strdup_printf("%02x%02x%02x%02x%02x%02x", self->mac_address[0], self->mac_address[1], + self->mac_address[2], self->mac_address[3], self->mac_address[4], self->mac_address[5]); + if (self->mac_address_str == NULL) { + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, + UCA_PHANTOM_COMMUNICATE_ERROR_START_RECORDING, "Failed to allocate memory for MAC address"); g_propagate_error(error_loc, phantom_error); return FALSE; } } - g_print("Starting xdata receiver thread\n"); self->data_receiver = g_thread_new("data_receiver", uca_phantom_communicate_accept_ximg, self); - g_print("Starting xdata unpacker thread\n"); self->data_unpacker = g_thread_new("data_unpacker", uca_phantom_communicate_unpack_ximg, self); - } - else { + } else { // Start new thread to read data - g_print("Starting data receiver thread\n"); self->data_receiver = g_thread_new("data_receiver", uca_phantom_communicate_accept_img, self); } + if (self->timestamping) { + // Start new thread to read timestamps + g_print("Starting timestamp thread\n"); + self->ts_receiver = g_thread_new("ts_receiver", uca_phantom_communicate_accept_timestamps, self); + } + + if (self->buffering) { + // Start new thread to buffer images + g_print("Starting buffering thread\n"); + self->buffering_thread = g_thread_new("buffering_thread", uca_phantom_communicate_buffer_images, self); + } + self->local_acquisition_state = ACQUIRING; return TRUE; } -/** - * @brief Generic grab image function - * - * @param self - * @param error_loc - * @return gboolean - */ -gboolean uca_phantom_communicate_grab_image(UcaPhantomCommunicate *self, gpointer data, GError **error_loc) { - static CineData *cine_data = NULL; +gboolean uca_phantom_communicate_grab_image(UcaPhantomCommunicate* self, gpointer data, GError** error_loc) +{ + static CineData* cine_data = NULL; // static GError *sub_error = NULL; - static GError *phantom_error = NULL; + static GError* phantom_error = NULL; + static guint prev_buffer_index = 0; g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); cine_data = g_async_queue_pop(self->unpacked_queue); if (cine_data == NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GRAB_IMAGE, "No image available."); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_GRAB_IMAGE, + "No image available."); g_propagate_error(error_loc, phantom_error); return FALSE; } // copy the image data to the output buffer // CAUTION : no verification is done on the size of the output buffer... - // This is dangerous as it as it puts the user in charge of allocating the right amount of memory - // with the good bit depth. + // This is dangerous as it as it puts the user in charge of allocating the + // right amount of memory with the good bit depth. // TODO : Mqybe consider GBytes for the output buffer ? memcpy(data, cine_data->UnpackedImages, cine_data->SizePerImageUnpacked); + // If we arrive in a new buffer, free the previous one + if (cine_data->buffer_index != prev_buffer_index) { + g_debug("Freeing buffer %d\n", prev_buffer_index); + g_free(g_ptr_array_remove_index(self->unpacked_images, prev_buffer_index)); + prev_buffer_index = cine_data->buffer_index; + } + // Free the CineData object g_free(cine_data); return TRUE; } -gboolean uca_phantom_communicate_stop_readout(UcaPhantomCommunicate *self, GError **error_loc) { +gboolean uca_phantom_communicate_grab_buffered_image (UcaPhantomCommunicate* self, gpointer data, GError** error_loc) +{ + static CineData* cine_data = NULL; + // static GError *sub_error = NULL; + static GError* phantom_error = NULL; + static guint prev_buffer_index = 0; + + g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); + + // CAUTION : no verification is done on the size of the output buffer... + // This is dangerous as it as it puts the user in charge of allocating the + // right amount of memory with the good bit depth. + // TODO : Mqybe consider GBytes for the output buffer ? + gsize image_size = self->settings.width * self->settings.height * self->settings.bit_depth / 8; + ringbuf_memcpy_from (self->ringbuf, data, image_size); + + // Free the CineData object + g_free(cine_data); + + return TRUE; +} + +gboolean uca_phantom_communicate_stop_readout(UcaPhantomCommunicate* self, GError** error_loc) +{ g_return_val_if_fail(error_loc == NULL || *error_loc == NULL, FALSE); g_return_val_if_fail(self->control_connection_state == CONNECTED, FALSE); g_return_val_if_fail(self->local_acquisition_state == ACQUIRING, FALSE); g_return_val_if_fail(self->phantom_acquisition_state == ACQUIRING, FALSE); - GError *sub_error = NULL; - GError *phantom_error = NULL; + GError* sub_error = NULL; + GError* phantom_error = NULL; // Push end request to the queue to unblock the data_receiver thread - InternalRequest *request = g_new0(InternalRequest, 1); + ImageRequest* request = g_new0(ImageRequest, 1); request->end_request = TRUE; request->nb_images = 0; request->img_format = 0; @@ -3017,7 +3152,8 @@ gboolean uca_phantom_communicate_stop_readout(UcaPhantomCommunicate *self, GErro sub_error = g_thread_join(self->data_receiver); if (sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_STOP_READOUT, "Failed to join data_receiver thread:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_STOP_READOUT, + "Failed to join data_receiver thread:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -3027,7 +3163,8 @@ gboolean uca_phantom_communicate_stop_readout(UcaPhantomCommunicate *self, GErro // Stop the data unpacker thread sub_error = g_thread_join(self->data_unpacker); if (sub_error != NULL) { - g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_STOP_READOUT, "Failed to join data_unpacker thread:\n\t> %s\n", sub_error->message); + g_set_error(&phantom_error, UCA_PHANTOM_COMMUNICATE_ERROR, UCA_PHANTOM_COMMUNICATE_ERROR_STOP_READOUT, + "Failed to join data_unpacker thread:\n\t> %s\n", sub_error->message); g_propagate_error(error_loc, phantom_error); g_clear_error(&sub_error); return FALSE; @@ -3036,7 +3173,6 @@ gboolean uca_phantom_communicate_stop_readout(UcaPhantomCommunicate *self, GErro self->phantom_acquisition_state = IDLE; self->local_acquisition_state = IDLE; - g_print("Readout stopped\n"); return TRUE; } diff --git a/uca-phantom-communicate.h b/uca-phantom-communicate.h index b55f251..dba4b34 100644 --- a/uca-phantom-communicate.h +++ b/uca-phantom-communicate.h @@ -32,6 +32,7 @@ typedef enum { UCA_PHANTOM_COMMUNICATE_ERROR_SET_VARIABLE, UCA_PHANTOM_COMMUNICATE_ERROR_RUN_COMMAND, UCA_PHANTOM_COMMUNICATE_ERROR_NOTIFY, + UCA_PHANTOM_COMMUNICATE_ERROR_SET_NB_CINES, UCA_PHANTOM_COMMUNICATE_ERROR_GET_SETTINGS, UCA_PHANTOM_COMMUNICATE_ERROR_SET_SETTINGS, UCA_PHANTOM_COMMUNICATE_ERROR_GET_RESOLUTION, @@ -52,6 +53,16 @@ typedef enum { UCA_PHANTOM_COMMUNICATE_ERROR_MAYBE_CORRUPTED } UcaPhantomCommunicateError; +/** + * @brief Enumeration of synchronization modes for Phantom camera image capture. + * + * This enumeration defines the possible synchronization modes that can be used when capturing images with the Phantom camera. + * + * SYNC_MODE_FREE_RUN: Free run synchronization mode. + * SYNC_MODE_FSYNC: Fsync synchronization mode. + * SYNC_MODE_IRIG: IRIG synchronization mode. + * SYNC_MODE_VIDEO_FRAME_RATE: Video frame rate synchronization mode. + */ typedef enum { SYNC_MODE_FREE_RUN = 0, SYNC_MODE_FSYNC, @@ -59,6 +70,17 @@ typedef enum { SYNC_MODE_VIDEO_FRAME_RATE, } SyncMode; +/** + * @brief Enumeration of acquisition modes for Phantom camera image capture. + * + * This enumeration defines the possible acquisition modes that can be used when capturing images with the Phantom camera. + * + * ACQUISITION_MODE_STANDARD: Standard acquisition mode. + * ACQUISITION_MODE_STANDARD_BINNED: Standard acquisition mode with binning. + * ACQUISITION_MODE_HS: High-speed acquisition mode. + * ACQUISITION_MODE_HS_BINNED: High-speed acquisition mode with binning. + * ACQUISITION_MODE_BRIGHT_FIELD: Bright field acquisition mode. + */ typedef enum { ACQUISITION_MODE_STANDARD = 0, ACQUISITION_MODE_STANDARD_BINNED = 2, @@ -67,48 +89,84 @@ typedef enum { ACQUISITION_MODE_BRIGHT_FIELD } AcquisitionMode; +/** + * @brief Enumeration of auto exposure modes for Phantom camera image capture. + * + * This enumeration defines the possible auto exposure modes that can be used when capturing images with the Phantom camera. + * + */ typedef enum { - AUTO_EXP_MODE_OFF = 0, - AUTO_EXP_MODE_AVERAGE, - AUTO_EXP_MODE_SPOT, - AUTO_EXP_MODE_CENTER + AUTO_EXP_MODE_OFF = 0, /**< Auto exposure is turned off. */ + AUTO_EXP_MODE_AVERAGE, /**< Auto exposure is based on the average brightness of the image. */ + AUTO_EXP_MODE_SPOT, /**< Auto exposure is based on a spot meter reading. */ + AUTO_EXP_MODE_CENTER /**< Auto exposure is based on the center of the image. */ } AutoExpMode; +/** + * @brief The bit depth of the image format. + * + * This field specifies the bit depth of the image (i.e. image format). + * For example, if the image format is 8-bit, this field will be set to 8. + * + */ typedef enum { - IMG_8, - IMG_8R, - IMG_P16, - IMG_P16R, - IMG_P10, - IMG_P12L + IMG_8, // 8 bits per pixel, FPN and PRNU corrected, linear, raw + IMG_8R, // 8 bits per pixel, uncorrected, linear, raw + IMG_P16, // 16 bits per pixel, FPN and PRNU corrected, linear, raw, little-endian. The range of values is 0-65535. + IMG_P16R, // Same as P16 but uncorrected + IMG_P10, // 10 bits per pixel packed into 32-bit big-endian words, FPN and PRNU corrected, non-linear, raw. + IMG_P12L // 12 bits per pixel packed into 32-bit big-endian words, FPN and PRNU corrected, linear, raw. } ImageFormat; +/** + * @brief Enumeration of timestamp formats for Phantom camera image capture. + * + * This enumeration defines the possible timestamp formats that can be requested when capturing images with the Phantom camera. + * + */ typedef enum { - TS_SHORT, - TS_SHORT32, - TS_LONG, - TS_LONG32, - TS_NONE // No timestamp is requested + TS_SHORT, /**< Short timestamp format. */ + TS_SHORT32, /**< 32-bit short timestamp format. */ + TS_LONG, /**< Long timestamp format. */ + TS_LONG32, /**< 32-bit long timestamp format. */ + TS_NONE /**< No timestamp is requested. */ } TimestampFormat; -typedef struct { - gchar *format_string; - guint bit_depth; - gfloat byte_depth; -} ImageFormatSpec; - -typedef struct { - gchar *format_string; - guint bit_depth; - gfloat byte_depth; -} TimestampSpec; +/** + * @brief Enumeration of IP source flags for the TCP connection. + * + * This enumeration defines the possible IP source flags that can be used to choose the IP source for the TCP connection + * in the `connect_datastream` function. + * + * USE_ENV: Use the IP address specified in the environment variable. + * USE_CLASS: Use the IP address specified in the class. + * USE_BCAST: Use the broadcast IP address. + * N_IP_FLAGS: The number of IP source flags. + */ +typedef enum { + USE_ENV, + USE_CLASS, + USE_BCAST, + N_IP_FLAGS +} IP_SOURCE_FLAGS; +/** + * @brief Struct containing capture settings for the Phantom camera. + * + * This struct contains various properties related to capturing images with the Phantom camera, + * including sensor pixel width and height, bit depth, trigger source and type, frames per second, + * exposure time, region of interest (ROI), focal length, aperture, EDR exposure time, shutter offset, + * auto exposure mode and compensation, number of pre- and post-trigger frames, current cine number, + * sync mode, acquisition mode, image format, and timestamp format. + * + * The list of properties is not exhaustive, and is subject to change. + */ typedef struct{ // base properties guint16 sensor_pixel_width, sensor_pixel_height, sensor_bit_depth; UcaCameraTriggerSource trigger_source; UcaCameraTriggerType trigger_type; - gfloat frames_per_second; + gdouble frames_per_second; gdouble exposure_time; gint roi_pixel_x, roi_pixel_y, roi_pixel_width, roi_pixel_height; guint roi_width_multiplier, roi_height_multiplier; @@ -127,40 +185,255 @@ typedef struct{ TimestampFormat timestamp_format; } CaptureSettings; +typedef struct { + gchar* format_string; + guint bit_depth; + gfloat byte_depth; +} ImageFormatSpec; + +typedef struct +{ + gchar* format_string; + gsize byte_size; +} TimestampSpec; + extern const ImageFormatSpec ImageFormatSpecs[]; extern const TimestampSpec TimestampSpecs[]; -/* - * Public methods -*/ +/** + * @brief Creates a new UcaPhantomCommunicate object. + * + * This function creates a new UcaPhantomCommunicate object. + * + * @return UcaPhantomCommunicate* A pointer to the newly created UcaPhantomCommunicate object. + */ UcaPhantomCommunicate *uca_phantom_communicate_new (void); + +/** + * @brief Connects to the TCP phantom control stream and returns a boolean indicating success or failure. + * + * You need to connect the controlstream before you can send commands to the phantom. + * + * @param self A pointer to the UcaPhantomCommunicate object. + * @param error_loc A pointer to a GError object that will be set if an error occurs. + * @return gboolean Returns TRUE if the connection was successful, FALSE otherwise. + * + * @note The caller is responsible for freeing the GError object if it is set. + */ gboolean uca_phantom_communicate_connect_controlstream (UcaPhantomCommunicate *self, GError **error_loc); + +/** + * @brief Opens TCP data connection with the Phantom camera. + * + * This function uses the port `self->data_port` to open a TCP connection with the Phantom camera. + * This connection only takes timestamps and images of bitdeph 8 or 16. + * + * @param self The UcaPhantomCommunicate object. + * @param error_loc A pointer to a GError pointer to store any errors that occur. + * @return TRUE if the socket was opened successfully, FALSE otherwise. + */ gboolean uca_phantom_communicate_connect_datastream(UcaPhantomCommunicate *self, GError **error_loc); + +/** + * @brief Opens a socket that accepts raw ethernet data frames from the Phantom. + * + * This function uses libpcap to capture ethernet frames from the NIC called `xnetcard`. + * + * @param self The UcaPhantomCommunicate object. + * @param error_loc A pointer to a GError pointer to store any errors that occur. + * @return TRUE if the socket was opened successfully, FALSE otherwise. + */ gboolean uca_phantom_communicate_connect_xdatastream (UcaPhantomCommunicate *self, GError **error_loc); -gboolean uca_phantom_communicate_run_command (UcaPhantomCommunicate *self, guint command_flag, PhantomReply *reply, GError **error_loc, ...); + +/** + * @brief Send a command to the phantom and get the reply + * + * @paragraph This function sends a command to the phantom and gets the reply. + * The command is specified by the command_flag. The reply is stored in the caller-owned reply struct. + * If the reply is NULL, a local one will be created. + * + * @param self + * @param command_flag + * @param command_arg + * @param reply (optional) + * @param error_loc + * @return gboolean + * + */ +gboolean uca_phantom_communicate_run_command(UcaPhantomCommunicate *self, guint command_flag, gchar* command_arg, PhantomReply *reply, GError **error_loc); + +/** + * @brief Disconnects the data stream from the phantom. + * + * This function closes the input data stream and sets the data connection state to DISCONNECTED. + * + * @param self The UcaPhantomCommunicate object. + * @param error_loc A pointer to a GError pointer to store any errors that occur. + * @return TRUE if the data stream was disconnected successfully, FALSE otherwise. + */ +gboolean uca_phantom_communicate_disconnect_datastream(UcaPhantomCommunicate *self, GError **error_loc); + +/** + * @brief Disconnects the xdata stream from the phantom. + * + * This function closes the pcap handle and sets the xdata connection state to DISCONNECTED. + * + * @param self The UcaPhantomCommunicate object. + * @param error_loc A pointer to a GError pointer to store any errors that occur. + * @return TRUE if the xdata stream was disconnected successfully, FALSE otherwise. + */ +gboolean uca_phantom_communicate_disconnect_xdatastream(UcaPhantomCommunicate *self, GError **error_loc); + +/** + * @brief Get the value of a variable from the phantom + * + * @paragraph This function will get the value of a unit variable from the phantom using the uca_phantom_communicate_run_command function. + * + * @param self + * @param variable_flag + * @param return_value + * @param error_loc + * @return gboolean + */ gboolean uca_phantom_communicate_get_variable (UcaPhantomCommunicate *self, guint variable_flag, GValue *return_value, GError **error); + +/** + * @brief Set the value of a variable on the phantom + * + * @param self + * @param variable_flag + * @param value + * @param error_loc + * @return gboolean + */ gboolean uca_phantom_communicate_set_variable (UcaPhantomCommunicate *self, guint variable_flag, const char *set_value, GError **error_loc); -void uca_phantom_communicate_print_capture_settings (UcaPhantomCommunicate *self); -gboolean uca_phantom_communicate_get_settings (UcaPhantomCommunicate *self, CaptureSettings *settings, GError **error_loc); + +/** + * @brief Helpher function to set the number of cines. + * + * This function sets the number of cines in the Phantom camera using set_variable function. + * + * @param self A pointer to the UcaPhantomCommunicate object. + * @param nb_cines The number of cines to set. + * @param error_loc A pointer to a GError object to store any errors that occur. + * + * @return TRUE if the number of cines was set successfully, FALSE otherwise. + * + * TODO: make sure the size of each cine is big enough to hold the images. + */ +gboolean uca_phantom_communicate_set_nb_cines (UcaPhantomCommunicate *self, guint nb_cines, GError **error_loc); + +/** + * @brief Sets the main capture settings of the Phantom camera. + * + * This function sets a few crucial settings for the Phantom camera, such as the sensor resolution, EDR exposure, shutter off, auto exposure mode, auto exposure compensation, and number of post trigger frames. + * + * @param self The UCA Phantom camera instance. + * @param ext_settings The external settings to be set. + * @param error_loc The location to store any errors that occur. + * + * @return TRUE if the settings were successfully set, FALSE otherwise. + */ gboolean uca_phantom_communicate_set_settings (UcaPhantomCommunicate *self, CaptureSettings *settings, GError **error_loc); + +/** + * @brief Starts the readout process for the UcaPhantomCommunicate object + * + * @details Starts a new thread to accept data from the camera (and unpack it if data is transmitted over 10Gb). + * If timestamping is enabled, it also starts a new thread to read timestamps. The function returns TRUE if the + * readout was successfully started, FALSE otherwise. + * + * @param self Pointer to the UcaPhantomCommunicate object + * @param error_loc Pointer to a GError object to store any errors that occur + * @return gboolean TRUE if the readout was successfully started, FALSE otherwise + */ gboolean uca_phantom_communicate_start_readout(UcaPhantomCommunicate *self, GError **error_loc); + +/** + * @brief Stop readout function + * + * This function stops (waits for) all the threads that were started by uca_phantom_communicate_start_readout. + * + * @param self Pointer to the UcaPhantomCommunicate object + * @param error_loc Pointer to a GError object to store any errors that occur + * @return gboolean TRUE if the readout was successfully stopped, FALSE otherwise + */ gboolean uca_phantom_communicate_stop_readout(UcaPhantomCommunicate *self, GError **error_loc); + +/** + * @brief Arms the phantom for acquisition in a cine partition. + * + * Once the phantom is armed, it will start recording in the cine partition at the rate set by the + * SyncMode flags. + * + * @param self The UcaPhantomCommunicate object + * @param cine The cine partition to record in + * @param error_loc A GError object to store the error in + * @return TRUE if the arm was successful, FALSE otherwise + */ gboolean uca_phantom_communicate_arm (UcaPhantomCommunicate *self, guint cine, GError **error_loc); +gboolean uca_phantom_communicate_disarm (UcaPhantomCommunicate* self, GError** error_loc); + +/** + * @brief Trigger the phantom's internal cine RAM partition. + * + * This will keep the frames stored in the cine RAM up until the trigger, and continue to store `post-frames` after the trigger. + * The total number of frames in the cine partition is therefore `pre-frames + post-frames`. + * + * @param self The UcaPhantomCommunicate object + * @param error_loc A GError object to store the error in + * @return TRUE if the trigger was successful, FALSE otherwise + */ gboolean uca_phantom_communicate_trigger (UcaPhantomCommunicate *self, GError **error_loc); -gboolean uca_phantom_communicate_trigger_ptframes (UcaPhantomCommunicate *self, guint ptframes, GError **error_loc); + +/** + * @brief Send a request for images to the Phantom camera and push an internal request to receive images in the program. + * + * This function requests images from the Phantom camera and pushes them to the request queue for processing. + * The function supports two modes of operation: one for 10Gb ethernet and one for 1Gb ethernet. + * + * @param self The UcaPhantomCommunicate object. + * @param settings The CaptureSettings object. Definition can be found in the UcaPhantomCommunicate header. + * @param error_loc A pointer to a GError object to store any errors that occur. + * @return TRUE if the request was successful, FALSE otherwise. + */ gboolean uca_phantom_communicate_request_images (UcaPhantomCommunicate *self, CaptureSettings *settings, GError **error_loc); +gboolean uca_phantom_communicate_request_images_buffered (UcaPhantomCommunicate *self, CaptureSettings *settings, GError **error_loc); +/** + * @brief Grab an image from the program's image queue + * + * @details Copys the image data allocated in the internal image queue to the provided pointer. + * This functions supposes that the provided pointer is large enough to hold the image data. + * + * @note This function will block until an image is available in the queue + * + * @param self Pointer to the UcaPhantomCommunicate object + * @param error_loc Pointer to a GError object to store any errors that occur + * @return gboolean TRUE if the readout was successfully stopped, FALSE otherwise + */ gboolean uca_phantom_communicate_grab_image (UcaPhantomCommunicate *self, gpointer data, GError **error_loc); +/** + * @brief Grab a buffered image from the program's ring buffer + * + * @details Copy a single image from the ring buffer to the provided pointer. + * + * @note This function will block until an image is available in the ring buffer + * + * @param self Pointer to the UcaPhantomCommunicate object + * @param error_loc Pointer to a GError object to store any errors that occur + * @return gboolean TRUE if the readout was successfully stopped, FALSE otherwise + */ +gboolean uca_phantom_communicate_grab_buffered_image (UcaPhantomCommunicate* self, gpointer data, GError** error_loc); -gboolean uca_phantom_communicate_get_mac_address (UcaPhantomCommunicate *self, GError **error_loc); - -typedef enum _IP_SOURCE_FLAGS { - USE_ENV, - USE_CLASS, - USE_BCAST, - N_IP_FLAGS -} IP_SOURCE_FLAGS; - +/** + * @brief The flags that represent phantom unit variables. + * + * The flags are used to identify the different types of variables that can be accessed or modified + * in a Phantom unit. They are used in conjunction with the phantom set and get functions. + * + * @see uca-phantom-commands.h and uca-phantom-variables.h for more information. + */ enum PhantomUnitIds { UNIT_INFO_SENSOR, UNIT_INFO_SNSVERSION,