kyle-west / racer.pi

Time derby car races with a raspberry pi
1 stars 1 forks source link

Architecture Discussion #22

Open thejefftrent opened 1 year ago

thejefftrent commented 1 year ago

I worry we are overly complicating things. My initial thoughts were to build this as a single desktop app all in Python. Keep it simple, and try to get an MVP out. Though, you are probably much more of a js/ts expert so it might feel easier to do it the way that you currently have it structured.

---
title: Architecture
---
flowchart TD
  subgraph Python Application
    subgraph Hardware IO
      track[Physical Race Track]
      gpio[GPIO Handler - Python]
    end

    subgraph Application Control
      controller[Controller]
      csv[CSV Download]
    end

    subgraph User Interface ptkinter?
        client[UI Interface]
    end
  end

  track-- Triggers Race -->gpio
  track-- Reads Lane Events -->gpio
  gpio-- Notifies Lane Times -->controller;
  gpio-- Notifies Race Start -->controller;
  client-- Store Race Results to FS -->csv;
  controller-- Realtime Race Results -->client;
  controller-- Lane States -.->client
  controller-- Lane States -.->client;
  client-- Converts Race Data -->controller;

I'm doing this quickly, but I think we could even simplify it further. We are really just reading the state from the gpio then storing the state of a race and either displaying it on the frontend or saving it to disk. But doing it all as different modules in Python instead of spinning up different services would increase simplicity a bit.

Admittedly there is some more business logic that is needed, but it isn't that complex. It also has the added benefit of just being much simpler to run for Ralph. If it is one app then he doesn't need to worry about spinning up three services.

flowchart TD
gpio[GPIO]
state[Race State]
ui[Display to user]
csv[Save to csv]

gpio --> state
state -->ui
state -->csv

I think we should at least simplify the backend and just do that in python

---
title: Architecture
---
flowchart TD
  subgraph Python Backend
    subgraph Hardware IO
      track[Physical Race Track]
      gpio[GPIO Handler - Python]
    end

    subgraph Application Control
      server[Application Server - Python django?]
      ws-server[Server Socket]
    end
  end

  subgraph User Interface
    subgraph Client Features
      client[Browser GUI]
      participants[Lane Assignments]
      csv[CSV Download]
    end

    ws-client[Client Socket]
  end

  track-- Triggers Race -->gpio
  track-- Reads Lane Events -->gpio
  gpio-- Notifies Lane Times -->server;
  gpio-- Notifies Race Start -->server;
  client-- Store Race Results to FS -->server;
  ws-client-- Realtime Race Results -->client;
  ws-server-- Lane States -.->ws-client
  server-- Lane States -.->ws-server;
  server-- Converts Race Data -->csv;
  participants-->client;

Let me know what you think, the api will be simple, which is why I am advocating for at least keeping that in python. Frontend js frameworks will be more powerful.

kyle-west commented 1 year ago

Honestly, thanks for your thoughts. I am open to making changes, but I would also like to point out why I did what I did. The main reason I went with the 3-service architecture was that it has a great separation of concerns.

The largest advantage with this is the ability to isolate each layer for testing and development. As long as each service fulfills their respective interoperability contracts, we can simulate other the layers as needed. This is a huge win in my eyes because it means that with a simple shell script (see the GPIO mock, for example) I don't need the physical track (or similar hardware) to iterate on the frontend bits. I can (and will) hook up buttons to the respective pins on my Pi and test it out, but I don't have to. This is invaluable to me because it shortens the development loop. As I make changes to the frontend, I can quickly test them with real-ish data being pushed to it. I've literally ran 100+ races as I've been building the GUI. It's so nice to not have to push physical buttons each time.

Instead of a desktop app, I wrote the fronted in Web Technologies because Grandpa wants multiple versions of the interface to display certain parts on different screens, etc. Web has some nice APIs for keeping those GUIs in sync and reactive to state changes. Plus I am quite comfortable there. I already got this working quite well and demoed it to Grandpa when he was here. It reacts to simulated lane events and runs through the BSA rules for hosting a derby. I haven't made it pretty yet, but functionally it is 95% complete for running a complete derby.

The web GUI needs a service to host the client files and push state via sockets to it. I wrote that part in Node because, again, I am quit familiar with it. I am not opposed to having a django service instead, but I already wrote it in Node. Aside from the CSV part (which is trivial to convert from the JSON structures I have) it's pretty much done too.

Your point about the 3 services running at the same time is a concern I have too. I haven't written that part, but I plan on having a single shell script (maybe ./start.sh?) that just boots up all the services at the same time.

GPIO should be in Python or C++. JavaScript has libraries for that but I hate them. Python's GPIO lib is first class.

thejefftrent commented 1 year ago

Instead of a desktop app, I wrote the fronted in Web Technologies because Grandpa wants multiple versions of the interface to display certain parts on different screens, etc.

That's a good point. We could have multiple windows in Python but that starts to be a pain to manage when we can just do different routes with a server. There is also the added benefit that other devices could pull up it up in the browser if we open it up to the local network.

Ignoring that you already have a lot of it built, there is no reason we couldn't take advantage of everything you listed for a 3 service architecture in a single application. We would just have mocks for each module and would be able to test them independently.

I haven't dug too deep into the code that you have already written, the architecture was a few layers more complicated than I was anticipating so I wanted to get a discussion about it going. Let me actually take the time to get it up and running, but I feel more comfortable now continuing with the current architecture.