Просмотр исходного кода

Message builder: auto select decoder and current decoded values.

This commit also fixes app_switch_view(), so that the
view private data is cleared before calling the exit/enter
callbacks.
antirez 3 лет назад
Родитель
Сommit
acd4329e08
5 измененных файлов с 111 добавлено и 20 удалено
  1. 12 5
      app.c
  2. 3 0
      app.h
  3. 55 3
      fields.c
  4. 3 3
      protocols/keeloq.c
  5. 38 9
      view_build.c

+ 12 - 5
app.c

@@ -81,6 +81,7 @@ static void input_callback(InputEvent* input_event, void* ctx)
  * special views ViewGoNext and ViewGoPrev in order to move to
  * the logical next/prev view. */
 static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
+    /* Switch to the specified view. */
     ProtoViewCurrentView old = app->current_view;
     if (switchto == ViewGoNext) {
         app->current_view++;
@@ -95,11 +96,22 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
     }
     ProtoViewCurrentView new = app->current_view;
 
+    /* Set the current subview of the view we just left to zero. This is
+     * the main subview of the old view. When re re-enter the view we are
+     * lefting, we want to see the main thing again. */
+    app->current_subview[old] = 0;
+
+    /* Reset the view private data each time, before calling the enter/exit
+     * callbacks that may want to setup some state. */
+    memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN);
+
     /* Call the enter/exit view callbacks if needed. */
     if (old == ViewDirectSampling) view_exit_direct_sampling(app);
     if (new == ViewDirectSampling) view_enter_direct_sampling(app);
     if (old == ViewBuildMessage) view_exit_build_message(app);
+    if (new == ViewBuildMessage) view_enter_build_message(app);
     if (old == ViewInfo) view_exit_info(app);
+
     /* The frequency/modulation settings are actually a single view:
      * as long as the user stays between the two modes of this view we
      * don't need to call the exit-view callback. */
@@ -107,11 +119,6 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
         (old == ViewModulationSettings && new != ViewFrequencySettings))
         view_exit_settings(app);
 
-    /* Set the current subview of the view we just left to zero, that is
-     * the main subview of the view. When re re-enter it we want to see
-     * the main thing. */
-    app->current_subview[old] = 0;
-    memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN);
     ui_dismiss_alert(app);
 }
 

+ 3 - 0
app.h

@@ -278,6 +278,7 @@ void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app);
 void process_input_direct_sampling(ProtoViewApp *app, InputEvent input);
 void render_view_build_message(Canvas *const canvas, ProtoViewApp *app);
 void process_input_build_message(ProtoViewApp *app, InputEvent input);
+void view_enter_build_message(ProtoViewApp *app);
 void view_exit_build_message(ProtoViewApp *app);
 void view_enter_direct_sampling(ProtoViewApp *app);
 void view_exit_direct_sampling(ProtoViewApp *app);
@@ -311,6 +312,8 @@ const char *field_get_type_name(ProtoViewField *f);
 int field_to_string(char *buf, size_t len, ProtoViewField *f);
 bool field_set_from_string(ProtoViewField *f, char *buf, size_t len);
 bool field_incr_value(ProtoViewField *f, int incr);
+void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, ProtoViewFieldSet *src);
+void field_set_from_field(ProtoViewField *dst, ProtoViewField *src);
 
 /* crc.c */
 uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly);

+ 55 - 3
fields.c

@@ -14,14 +14,20 @@ static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) {
     return f;
 }
 
-/* Free a field an associated data. */
-static void field_free(ProtoViewField *f) {
-    free(f->name);
+/* Free only the auxiliary data of a field, used to represent the
+ * current type. Name and type are not touched. */
+static void field_free_aux_data(ProtoViewField *f) {
     switch(f->type) {
     case FieldTypeStr: free(f->str); break;
     case FieldTypeBytes: free(f->bytes); break;
     default: break; // Nothing to free for other types.
     }
+}
+
+/* Free a field an associated data. */
+static void field_free(ProtoViewField *f) {
+    field_free_aux_data(f);
+    free(f->name);
     free(f);
 }
 
@@ -162,6 +168,34 @@ bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) {
     return true;
 }
 
+/* Set the 'dst' field to contain a copy of the value of the 'src'
+ * field. The field name is not modified. */
+void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) {
+    field_free_aux_data(dst);
+    dst->type = src->type;
+    dst->len = src->len;
+    switch(src->type)  {
+    case FieldTypeStr:
+        dst->str = strdup(src->str);
+        break;
+    case FieldTypeBytes:
+        dst->bytes = malloc(src->len);
+        memcpy(dst->bytes,src->bytes,dst->len);
+        break;
+    case FieldTypeSignedInt:
+        dst->value = src->value;
+        break;
+    case FieldTypeUnsignedInt:
+    case FieldTypeBinary:
+    case FieldTypeHex:
+        dst->uvalue = src->uvalue;
+        break;
+    case FieldTypeFloat:
+        dst->fvalue = src->fvalue;
+        break;
+    }
+}
+
 /* Increment the specified field value of 'incr'. If the field type
  * does not support increments false is returned, otherwise the
  * action is performed. */
@@ -304,3 +338,21 @@ void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint
     f->len = digits_after_dot;
     fieldset_add_field(fs,f);
 }
+
+/* For each field of the destination filedset 'dst', look for a matching
+ * field name/type in the source fieldset 'src', and if one is found copy
+ * its value into the 'dst' field. */
+void fieldset_copy_matching_fields(ProtoViewFieldSet *dst,
+                                   ProtoViewFieldSet *src)
+{
+    for (uint32_t j = 0; j < dst->numfields; j++) {
+        for (uint32_t i = 0; i < src->numfields; i++) {
+            if (dst->fields[j]->type == src->fields[i]->type &&
+                !strcmp(dst->fields[j]->name,src->fields[i]->name))
+            {
+                field_set_from_field(dst->fields[j],
+                                     src->fields[i]);
+            }
+        }
+    }
+}

+ 3 - 3
protocols/keeloq.c

@@ -64,11 +64,11 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
     int lowbat = (raw[8]&0x1) == 0; // Actual bit meaning: good battery level
     int alwaysone = (raw[8]&0x2) != 0;
 
-    fieldset_add_bytes(info->fieldset,"encr",raw,4*2);
+    fieldset_add_bytes(info->fieldset,"encr",raw,8);
     raw[7] = raw[7]<<4; // Make ID bits contiguous
     fieldset_add_bytes(info->fieldset,"id",raw+4,7); // 28 bits, 7 nibbles
-    fieldset_add_bin(info->fieldset,"s2 s1 s0 s3",buttons,4);
-    fieldset_add_bin(info->fieldset,"low battery",lowbat,0);
+    fieldset_add_bin(info->fieldset,"s[2,1,0,3]",buttons,4);
+    fieldset_add_bin(info->fieldset,"low battery",lowbat,1);
     fieldset_add_bin(info->fieldset,"always one",alwaysone,1);
     return true;
 }

+ 38 - 9
view_build.c

@@ -52,11 +52,6 @@ static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app)
     canvas_set_font(canvas, FontSecondary);
     canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose");
 
-    // When entering the view, the current decoder is just set to zero.
-    // Seek the next valid if needed.
-    if (Decoders[privdata->cur_decoder]->get_fields == NULL)
-        select_next_decoder(app);
-
     canvas_set_font(canvas, FontPrimary);
     canvas_draw_str_aligned(canvas,64,38,AlignCenter,AlignCenter,
         Decoders[privdata->cur_decoder]->name);
@@ -116,10 +111,22 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
             privdata->decoder = Decoders[privdata->cur_decoder];
             privdata->fieldset = fieldset_new();
             privdata->decoder->get_fields(privdata->fieldset);
-            // Now we use the subview system in order to protect the
-            // message editing mode from accidental < or > presses.
-            // Since we are technically into a subview now, we'll have
-            // control of < and >.
+
+            /* If the currently decoded message was produced with the
+             * same decoder the user selected, let's populate the
+             * defaults with the current values. So the user will
+             * actaully edit the current message. */
+            if (app->signal_decoded &&
+                app->msg_info->decoder == privdata->decoder)
+            {
+                fieldset_copy_matching_fields(privdata->fieldset,
+                    app->msg_info->fieldset);
+            }
+
+            /* Now we use the subview system in order to protect the
+               message editing mode from accidental < or > presses.
+               Since we are technically into a subview now, we'll have
+               control of < and >. */
             InputEvent ii = {.type = InputTypePress, .key = InputKeyDown};
             ui_process_subview_updown(app,ii,2);
         } else if (input.key == InputKeyDown) {
@@ -216,6 +223,28 @@ void process_input_build_message(ProtoViewApp *app, InputEvent input) {
         process_input_select_decoder(app,input);
 }
 
+/* Enter view callback. */
+void view_enter_build_message(ProtoViewApp *app) {
+    BuildViewPrivData *privdata = app->view_privdata;
+
+    // When we enter the view, the current decoder is just set to zero.
+    // Seek the next valid if needed.
+    if (Decoders[privdata->cur_decoder]->get_fields == NULL) {
+        select_next_decoder(app);
+    }
+
+    // However if there is currently a decoded message, and the
+    // decoder of such message supports message building, let's
+    // select it.
+    if (app->signal_decoded &&
+        app->msg_info->decoder->get_fields &&
+        app->msg_info->decoder->build_message)
+    {
+        while(Decoders[privdata->cur_decoder] != app->msg_info->decoder)
+            select_next_decoder(app);
+    }
+}
+
 /* Called on exit for cleanup. */
 void view_exit_build_message(ProtoViewApp *app) {
     BuildViewPrivData *privdata = app->view_privdata;