Browse Source

views/trade: Cease my abuse of View model

After converting select_pokemon, it only makes sense to clean up
trade as well. There were a number of globals vars passed in the
main PokemonFap struct that are only used by trade and have been
moved to the View model data. There are also currently still some
globals vars in the trade code that could either be relocated to
specific functions or moved in to the trade View model.

This also adds a periodic timer to the trade view. The functions
that get called through the lifetime of the trade process are
mostly in an interrupt context. This means we cannot request a
View update via with_view_model(). In order to handle regular
drawing, a timer is used to call a function out of an interrupt
context that does a null with_view_model() to simply trigger a
redraw.
Kris Bahnsen 2 years ago
parent
commit
5884a74593
2 changed files with 102 additions and 49 deletions
  1. 0 8
      pokemon_app.h
  2. 102 41
      views/trade.c

+ 0 - 8
pokemon_app.h

@@ -88,14 +88,6 @@ struct pokemon_fap {
     /* The currently selected pokemon */
     int curr_pokemon;
 
-    /* Some state tracking */
-    /* This, combined with some globals in trade.cpp, can probably be better
-     * consolidated at some point.
-     */
-    bool trading;
-    bool connected;
-    render_gameboy_state_t gameboy_status;
-
     /* TODO: Other variables will end up here, like selected level, EV/IV,
      * moveset, etc. Likely will want to be another sub struct similar to
      * the actual pokemon data structure.

+ 102 - 41
views/trade.c

@@ -1,4 +1,6 @@
 #include <furi_hal_light.h>
+#include <furi.h>
+
 #include <gui/view.h>
 #include <pokemon_icons.h>
 
@@ -31,6 +33,15 @@
 
 #define TRADE_CENTRE_WAIT 0xFD
 
+struct trade_model {
+    bool trading;
+    bool connected;
+    render_gameboy_state_t gameboy_status;
+    uint8_t curr_pokemon;
+    const PokemonTable* pokemon_table;
+    FuriTimer* draw_timer;
+};
+
 typedef unsigned char byte;
 typedef enum { NOT_CONNECTED, CONNECTED, TRADE_CENTRE, COLOSSEUM } connection_state_t;
 typedef enum {
@@ -49,15 +60,17 @@ typedef enum {
 } trade_centre_state_t;
 
 /* TODO: Convert all of these to be maintained in a struct in the Trade context */
-uint8_t out_data = 0;
-uint8_t in_data = 0;
-uint8_t shift = 0;
-uint32_t time = 0;
-volatile int counter = 0;
-volatile bool procesing = true;
-volatile connection_state_t connection_state = NOT_CONNECTED;
-volatile trade_centre_state_t trade_centre_state = INIT;
-unsigned char INPUT_BLOCK[405];
+uint8_t out_data = 0; // Should be able to be made as part of view model or static in used function
+uint8_t in_data = 0; //Should be able to be made as part of view model, is used in multiple funcs
+uint8_t shift = 0; //Should be able to be made as part of view model, is used in multiple funcs
+uint32_t time = 0; //Should be able to be made static in used function
+volatile int counter = 0; // Should be able to be made static in used function
+volatile bool procesing = true; // Review this vars use, it could potentially be removed
+volatile connection_state_t connection_state =
+    NOT_CONNECTED; // Should be made in to view model struct
+volatile trade_centre_state_t trade_centre_state =
+    INIT; // Should be able to be made part of view model
+unsigned char INPUT_BLOCK[405]; // Put this in pokemon_fap? Not sure yet
 
 void screen_gameboy_connect(Canvas* const canvas) {
     canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -80,11 +93,12 @@ int time_in_seconds = 0;
 
 static void trade_draw_callback(Canvas* canvas, void* model) {
     const char* gameboy_status_text = NULL;
-    PokemonFap* pokemon_fap = *(PokemonFap**)model;
+    struct trade_model* view_model = model;
+    uint8_t curr_pokemon = view_model->curr_pokemon;
 
     canvas_clear(canvas);
-    if(!pokemon_fap->trading) {
-        if(!pokemon_fap->connected) {
+    if(!view_model->trading) {
+        if(!view_model->connected) {
             furi_hal_light_set(LightGreen, 0x00);
             furi_hal_light_set(LightBlue, 0x00);
             furi_hal_light_set(LightRed, 0xff);
@@ -96,7 +110,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) {
             screen_gameboy_connected(canvas);
         }
     } else {
-        switch(pokemon_fap->gameboy_status) {
+        switch(view_model->gameboy_status) {
         case GAMEBOY_TRADING:
             furi_hal_light_set(LightGreen, 0x00);
             furi_hal_light_set(LightRed, 0x00);
@@ -111,8 +125,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) {
         case GAMEBOY_READY:
         case GAMEBOY_WAITING:
         case GAMEBOY_SEND:
-            canvas_draw_icon(
-                canvas, 38, 11, pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].icon);
+            canvas_draw_icon(canvas, 38, 11, view_model->pokemon_table[curr_pokemon].icon);
             break;
         default:
             // Default state added to eliminated enum warning
@@ -123,7 +136,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) {
         canvas_draw_frame(canvas, 0, 0, 128, 64);
         canvas_draw_icon(canvas, 24, 0, &I_Space_80x18);
 
-        switch(pokemon_fap->gameboy_status) {
+        switch(view_model->gameboy_status) {
         case GAMEBOY_READY:
             gameboy_status_text = "READY";
             break;
@@ -217,10 +230,17 @@ byte getMenuResponse(byte in) {
 byte getTradeCentreResponse(byte in, void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     uint8_t* trade_block_flat = (uint8_t*)pokemon_fap->trade_block;
+    render_gameboy_state_t gameboy_status;
     byte send = in;
 
     furi_assert(context);
 
+    with_view_model(
+        pokemon_fap->trade_view,
+        struct trade_model * model,
+        { gameboy_status = model->gameboy_status; },
+        false);
+
     switch(trade_centre_state) {
     case INIT:
         // TODO: What does this value of in mean?
@@ -229,7 +249,7 @@ byte getTradeCentreResponse(byte in, void* context) {
             if(counter == 5) {
                 trade_centre_state = READY_TO_GO;
                 //  CLICK EN LA MESA
-                pokemon_fap->gameboy_status = GAMEBOY_READY;
+                gameboy_status = GAMEBOY_READY;
             }
             counter++;
         }
@@ -250,7 +270,7 @@ byte getTradeCentreResponse(byte in, void* context) {
         if((in & 0xF0) == 0xF0) {
             if(counter == 5) {
                 trade_centre_state = WAITING_TO_SEND_DATA;
-                pokemon_fap->gameboy_status = GAMEBOY_WAITING;
+                gameboy_status = GAMEBOY_WAITING;
             }
             counter++;
         }
@@ -290,10 +310,10 @@ byte getTradeCentreResponse(byte in, void* context) {
         if(in == 0x6F) {
             trade_centre_state = READY_TO_GO;
             send = 0x6F;
-            pokemon_fap->gameboy_status = GAMEBOY_TRADE_READY;
+            gameboy_status = GAMEBOY_TRADE_READY;
         } else if((in & 0x60) == 0x60) {
             send = 0x60; // first pokemon
-            pokemon_fap->gameboy_status = GAMEBOY_SEND;
+            gameboy_status = GAMEBOY_SEND;
         } else if(in == 0x00) {
             send = 0;
             trade_centre_state = TRADE_CONFIRMATION;
@@ -303,7 +323,7 @@ byte getTradeCentreResponse(byte in, void* context) {
     case TRADE_CONFIRMATION:
         if(in == 0x61) {
             trade_centre_state = TRADE_PENDING;
-            pokemon_fap->gameboy_status = GAMEBOY_PENDING;
+            gameboy_status = GAMEBOY_PENDING;
         } else if((in & 0x60) == 0x60) {
             trade_centre_state = DONE;
         }
@@ -313,7 +333,7 @@ byte getTradeCentreResponse(byte in, void* context) {
         if(in == 0x00) {
             send = 0;
             trade_centre_state = INIT;
-            pokemon_fap->gameboy_status = GAMEBOY_TRADING;
+            gameboy_status = GAMEBOY_TRADING;
         }
         break;
 
@@ -322,12 +342,29 @@ byte getTradeCentreResponse(byte in, void* context) {
         break;
     }
 
+    with_view_model(
+        pokemon_fap->trade_view,
+        struct trade_model * model,
+        { model->gameboy_status = gameboy_status; },
+        false);
+
     return send;
 }
 
 void transferBit(void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     furi_assert(context);
+    bool connected;
+    bool trading;
+
+    with_view_model(
+        pokemon_fap->trade_view,
+        struct trade_model * model,
+        {
+            connected = model->connected;
+            trading = model->trading;
+        },
+        false);
 
     byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI);
     in_data |= raw_data << (7 - shift);
@@ -335,11 +372,11 @@ void transferBit(void* context) {
         shift = 0;
         switch(connection_state) {
         case NOT_CONNECTED:
-            pokemon_fap->connected = false;
+            connected = false;
             out_data = getConnectResponse(in_data);
             break;
         case CONNECTED:
-            pokemon_fap->connected = true;
+            connected = true;
             out_data = getMenuResponse(in_data);
             break;
         case TRADE_CENTRE:
@@ -361,9 +398,18 @@ void transferBit(void* context) {
         DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary)
     // TODO: The above comment doesn't make sense as DELAY_MICROSECONDS is defined as 15
 
-    if(trade_centre_state == READY_TO_GO) pokemon_fap->trading = true;
+    if(trade_centre_state == READY_TO_GO) trading = true;
 
     out_data = out_data << 1;
+
+    with_view_model(
+        pokemon_fap->trade_view,
+        struct trade_model * model,
+        {
+            model->connected = connected;
+            model->trading = trading;
+        },
+        false);
 }
 
 void input_clk_gameboy(void* context) {
@@ -382,13 +428,32 @@ void input_clk_gameboy(void* context) {
     time = micros();
 }
 
+void trade_draw_timer_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    with_view_model(
+        pokemon_fap->trade_view, struct trade_model * model, { UNUSED(model); }, true);
+}
+
 void trade_enter_callback(void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     furi_assert(context);
 
-    pokemon_fap->trading = false;
-    pokemon_fap->connected = false;
-    pokemon_fap->gameboy_status = GAMEBOY_INITIAL;
+    with_view_model(
+        pokemon_fap->trade_view,
+        struct trade_model * model,
+        {
+            model->trading = false;
+            model->connected = false;
+            model->gameboy_status = GAMEBOY_INITIAL;
+            model->pokemon_table = pokemon_fap->pokemon_table;
+            model->curr_pokemon = (uint8_t)pokemon_fap->curr_pokemon;
+            model->draw_timer =
+                furi_timer_alloc(trade_draw_timer_callback, FuriTimerTypePeriodic, pokemon_fap);
+            /* Every 100 ms, trigger a draw update */
+            furi_timer_start(model->draw_timer, 100);
+        },
+        true);
 
     // B3 (Pin6) / SO (2)
     furi_hal_gpio_write(&GAME_BOY_SO, false);
@@ -397,18 +462,9 @@ void trade_enter_callback(void* context) {
     furi_hal_gpio_write(&GAME_BOY_SI, false);
     furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
     // // C3 (Pin7) / CLK (5)
-    furi_hal_gpio_init(
-        &GAME_BOY_CLK,
-        GpioModeInterruptRise,
-        GpioPullNo,
-        GpioSpeedVeryHigh); // <-- This line causes the "OK" to stop functioning when exiting the application, so a reboot of the Flipper Zero is required.
+    furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeInterruptRise, GpioPullNo, GpioSpeedVeryHigh);
     furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
     furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, pokemon_fap);
-
-    // furi_hal_gpio_disable_int_callback(&GAME_BOY_CLK);
-    // furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
-    // Reset GPIO pins to default state
-    // furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 }
 
 void disconnect_pin(const GpioPin* pin) {
@@ -417,11 +473,18 @@ void disconnect_pin(const GpioPin* pin) {
 }
 
 void trade_exit_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
     furi_assert(context);
     procesing = false;
     furi_hal_light_set(LightGreen, 0x00);
     furi_hal_light_set(LightBlue, 0x00);
     furi_hal_light_set(LightRed, 0x00);
+    /* Stop the timer, and deallocate it as the enter callback allocates it on entry */
+    with_view_model(
+        pokemon_fap->trade_view,
+        struct trade_model * model,
+        { furi_timer_free(model->draw_timer); },
+        false);
 }
 
 View* trade_alloc(PokemonFap* pokemon_fap) {
@@ -431,9 +494,7 @@ View* trade_alloc(PokemonFap* pokemon_fap) {
     procesing = true;
 
     view_set_context(view, pokemon_fap);
-    view_allocate_model(view, ViewModelTypeLockFree, sizeof(PokemonFap**));
-    with_view_model(
-        view, PokemonFap** model_fap, { *model_fap = pokemon_fap; }, false);
+    view_allocate_model(view, ViewModelTypeLockFree, sizeof(struct trade_model));
 
     view_set_draw_callback(view, trade_draw_callback);
     view_set_enter_callback(view, trade_enter_callback);