Skip to content

Commit

Permalink
Merge pull request #12 from hytech-racing/testing_and_documenting
Browse files Browse the repository at this point in the history
Testing and documenting
  • Loading branch information
RCMast3r committed Feb 5, 2024
2 parents f6ff9c1 + 7c53659 commit 31ffd77
Show file tree
Hide file tree
Showing 33 changed files with 637 additions and 357 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/platformio.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: PlatformIO CI

on: [push, pull_request]

jobs:
run_build_and_tests:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Install PlatformIO CLI
run: pip install platformio

- name: Run PlatformIO build for teensy
run: pio run -e teensy41

- name: Run Tests
run: pio test -e test_env
124 changes: 78 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
new MCU code:
- component definition:
an abstract sub-system of the physical car or the code that requires logic to be evaluated by the MCU to determine what input to give it or logic required to handle output from.
- driver definition:
- code required to purely unpack / pack data into internal structs / classes for use by components or logic
- I think the best way to handle the driver level is that all the drivers

- architecture:

- over-arching state machine
- components level
- inverters (multiple)
- pedals
- torque / speed controller
- driver dashboard

- driver level:
- hytech_can driver
- spi drivers: SPI adcs for load cells, steering input, glv, etc.

### outline

Levels represent the layer of abstraction they are on. The reason for keeping track of this is to minimize the layers of abstraction for ease of understanding while also creating logical structure and getting maximum benefit per abstraction.
Expand All @@ -29,23 +9,23 @@ Levels represent the layer of abstraction they are on. The reason for keeping tr


#### level 1: state machine goals for interface design and implementation
- __Reason for abstraction__: allows for easy swapping and adding of different portable components and better [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) from [business logic](https://www.techtarget.com/whatis/definition/business-logic) of the car to the business logic of the component.
- __Reason for abstraction__: allows for easy swapping and adding of different portable systems and better [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) from [business logic](https://www.techtarget.com/whatis/definition/business-logic) of the car to the business logic of the system.


Any firmware project that needs to have different states needs each component that creates outputs and / or controls real components of the car needs can be thought of as each component being controlled by the state machine. What I am thinking is that in a similar fashion to the shared bus, each component can contain a shared pointer to the state machine. The component can know what state the car is in and based on the state it can determine how to behave. Obviously the state machine also needs to know about what the component is doing as well to determine the state, so the component also needs to be able to pass back data to the state machine.
Any firmware project that needs to have different states needs each system that creates outputs and / or controls real systems of the car needs can be thought of as each system being controlled by the state machine. What I am thinking is that in a similar fashion to the shared bus, each system can contain a shared pointer to the state machine. The system can know what state the car is in and based on the state it can determine how to behave. Obviously the state machine also needs to know about what the system is doing as well to determine the state, so the system also needs to be able to pass back data to the state machine.

For example, our state machine needs to handle understand the state of the pedals component. The pedals dont know about the state of the car, but it does know whether or not the pedals are outputting valid data. Each component can manage their own state and the abstract component base class could contain the set of component-agnostic states through which the statemachine evaluates.
For example, our state machine needs to handle understand the state of the pedals system. The pedals dont know about the state of the car, but it does know whether or not the pedals are outputting valid data. Each system can manage their own state and the abstract system base class could contain the set of system-agnostic states through which the statemachine evaluates.

It is only within the logic of our state machine that the components are allowed to communicate with one another.
It is only within the logic of our state machine that the systems are allowed to communicate with one another.

The main idea is that each firmware project has a specific implementation of a state machine, however the components are portable between firmware projects. Additionally, the components remain as concrete
The main idea is that each firmware project has a specific implementation of a state machine, however the systems are portable between firmware projects. Additionally, the systems remain as concrete




```mermaid
---
title: state machine and component state abstraction
title: state machine and system state abstraction
---
classDiagram
Expand All @@ -59,19 +39,16 @@ classDiagram
}
class StateMachine {
BMSComponent* bms
VectornavComponent* vectornav
PedalsComponent* pedals
DashComponent* dashboard
VectornavSystem* vectornav
PedalsSystem* pedals
DashSystem* dashboard
InverterComponent* left_front
InverterComponent* right_front
InverterComponent* left_rear
InverterComponent* right_rear
DrivetrainSystem* drivetrain
TorqueVectoringControllerComponent* tvc
LaunchControlComponent* launch_control
TorqueVectoringControllerSystem* tvc
LaunchControlSystem* launch_control
car_state state
void init()
Expand All @@ -86,19 +63,19 @@ classDiagram



#### level 2 portable components: interfaces, logic and structure
#### level 2 portable Systems: interfaces, logic and structure

- __Reason for abstraction__: these components allow us to have board portable pieces so that when newer iterations of boards are made, the same components that the previous iteration handled can be kept while only the hardware specific code changes.
- __Reason for abstraction__: these Systems allow us to have board portable pieces so that when newer iterations of boards are made, the same systems that the previous iteration handled can be kept while only the hardware specific code changes.

For instance, when a new MCU board is created with a new steering sensor input, code within the controller components will not need to change, only that a new sensor component will be used within the state machine to feed the controller input.
For instance, when a new MCU board is created with a new steering sensor input, code within the controller systems will not need to change, only that a new sensor system will be used within the state machine to feed the controller input.

below are some hypothetical component class definitions.
below are some hypothetical system class definitions.
```mermaid
---
title: components
title: systems
---
classDiagram
class PedalsComponent{
class PedalsSystem{
-void validate_accel_pedals()
-void validate_brake_pedals()
Expand All @@ -107,7 +84,7 @@ classDiagram
+float get_desired_throttle()
+void validate()
}
class TorqueVectoringControllerComponent{
class TorqueVectoringControllerSystem{
+void init(torque_vectoring_params params)
+void set_state_estimate(car_state state)
Expand All @@ -117,7 +94,7 @@ classDiagram
-void run_pid()
}
class LaunchControlComponent{
class LaunchControlSystem{
+void init(launch_control_params params)
+void set_state_estimate(car_state state)
Expand All @@ -130,7 +107,7 @@ classDiagram

#### level 2 SPI / i2c data bus abstraction from hardware specific implementations

- __Reason for abstraction__: this allows us to create a specific type of component that uses a shared resource, for example multiple sensors on a SPI bus, that each have their own scaling to produce data for feeding other components.
- __Reason for abstraction__: this allows us to create a specific type of system that uses a shared resource, for example multiple sensors on a SPI bus, that each have their own scaling to produce data for feeding other systems.

This is currently aimed at our use of a SPI bus. The read data functions are what convert the data gotten from the shared bus to the real-world values for each one of the sensors. This was being attempted with ADC_SPI versus STEERING_SPI using just copies of the class.

Expand Down Expand Up @@ -159,4 +136,59 @@ classDiagram
Sensor <|-- SteeringSensor : implements
Sensor <|-- CurrentSensor : implements
```
```


### state machine documentation

```mermaid
stateDiagram-v2
startup : STARTUP
trac_sys_na : TRACTIVE_SYSTEM_NOT_ACTIVE
trac_sys_a : TRACTIVE_SYSTEM_ACTIVE
inv_en : ENABLING_INVERTERS
dt_q_dc_on : WAITING_DRIVETRAIN_QUIT_DC_ON
dt_en : WAITING_DRIVETRAIN_ENABLED
rtd_s : WAITING_READY_TO_DRIVE_SOUND
rtd : READY_TO_DRIVE
startup --> trac_sys_na: first tick of state machine
trac_sys_na --> trac_sys_a: drivetrain voltage over threshold
trac_sys_a --> trac_sys_na: drivetrain voltage _not_ over threshold
trac_sys_a --> inv_en: brake and start button pressed
inv_en --> trac_sys_na: drivetrain voltage _not_ over threshold
inv_en --> dt_q_dc_on: drivetrain ready
dt_q_dc_on --> trac_sys_a: drivetrain init timeout
dt_q_dc_on --> dt_en: drivetrain quit dc statuses on
dt_en --> trac_sys_a: drivetrain initialization timeout
dt_en --> rtd_s: drivetrain enabled
rtd_s --> trac_sys_na: drivetrain voltage _not_ over threshold
rtd_s --> rtd: buzzer done buzzing
rtd --> trac_sys_na: drivetrain voltage _not_ over threshold
rtd --> trac_sys_a: drivetrain error occured
```
#### running the tests

This repo uses platformio testing for CI unit testing. these tests can be run locally with `pio test -e test_env`. The CI checks to ensure that the code both compiles for the teensy and ensures that the tests are passing.



#### notes
new MCU code:
- system definition:
an abstract sub-system of the physical car or the code that requires logic to be evaluated by the MCU to determine what input to give it or logic required to handle output from.
- interface definition:
- code required to purely unpack / pack data into internal structs / classes for use by systems or logic

- architecture:

- over-arching state machine
- systems level
- inverters (multiple)
- pedals
- torque / speed controller
- dashboard interface

- interface level:
- hytech_can interface
- spi interfaces: SPI adcs for load cells, steering input, glv, etc.
11 changes: 11 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
testing strategy:
- using GTest for mocking systems
- custom re-definitions of interfaces (for now)
- cant use GTest mocking easily for this I dont think

- the mock interfaces will just be empty classes p much to have something that the systems classes can have pointers to if they own a pointer to an interface

- the mock systems will use the gmock lib for better mocking of systems for system to system interaction in the test env



Empty file added TODO.md
Empty file.
11 changes: 6 additions & 5 deletions lib/interfaces/include/DashboardInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "MessageQueueDefine.h"
#include "FlexCAN_T4.h"
#include "ht_can.h"
#include "hytech.h"

enum DialMode_e
{
Expand Down Expand Up @@ -37,7 +37,7 @@ enum DashLED_e
MC_ERROR_LED,
IMD_LED,
AMS_LED,
}
};

struct DashButtons_s
{
Expand Down Expand Up @@ -78,7 +78,7 @@ class DashboardInterface

public:

Dashboard(CANBufferType *msg_output_queue)
DashboardInterface(CANBufferType *msg_output_queue)
{
msg_queue_ = msg_output_queue;
};
Expand All @@ -98,8 +98,9 @@ class DashboardInterface
bool torqueLoadingButtonPressed();
bool nightModeButtonPressed();
bool torqueVectoringOffButtonPressed();

void soundBuzzer();
bool shutdownHAboveThreshold();
void soundBuzzer(bool s);
bool checkBuzzer();

// LEDs in same order as dash rev. 7 placement

Expand Down
39 changes: 20 additions & 19 deletions lib/interfaces/include/MCUInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <stdint.h>
#include "FlexCAN_T4.h"
#include "HyTech_CAN.h"
#include "ht_can.h"
#include "hytech.h"
#include "SysClock.h"
#include "MCUStateMachine.h"

Expand Down Expand Up @@ -52,24 +52,25 @@ class MCUInterface
int pin_inv_24V_en_;

public:
MCUInterface(message_queue *msg_output_queue, int bms_pin, int imd_pin, int bspd_pin, int sw_ok_pin, int bots_ok_pin, int brake_light_pin, int inv_pin, int inv_24V_pin):
msg_queue_(msg_output_queue),
pin_bms_ok_read_(bms_pin),
pin_imd_ok_read_(imd_pin),
pin_bspd_ok_read_(bspd_pin),
pin_software_ok_read_(sw_ok_pin),
pin_bots_ok_read_(bots_ok_pin),
pin_brake_light_ctrl_(brake_light_pin),
pin_inv_en_(inv_pin),
pin_inv_24V_en_(inv_24V_pin)
{
// Set pin mode
pinMode(pin_brake_light_ctrl_, OUTPUT);
pinMode(pin_inv_en_, OUTPUT);
pinMode(pin_inv_24V_en_, OUTPUT);
};
MCUInterface(message_queue *msg_output_queue):
MCUInterface(msg_output_queue, DEFAULT_BMS_OK_READ, DEFAULT_IMD_OK_READ, DEFAULT_BSPD_OK_READ, DEFAULT_SOFTWARE_OK_READ, DEFAULT_BOTS_OK_READ, DEFAULT_BRAKE_LIGHT_CTRL, DEFAULT_INVERTER_EN, DEFAULT_INVERTER_24V_EN) {};
MCUInterface(){};
// MCUInterface(message_queue *msg_output_queue, int bms_pin, int imd_pin, int bspd_pin, int sw_ok_pin, int bots_ok_pin, int brake_light_pin, int inv_pin, int inv_24V_pin):
// msg_queue_(msg_output_queue),
// pin_bms_ok_read_(bms_pin),
// pin_imd_ok_read_(imd_pin),
// pin_bspd_ok_read_(bspd_pin),
// pin_software_ok_read_(sw_ok_pin),
// pin_bots_ok_read_(bots_ok_pin),
// pin_brake_light_ctrl_(brake_light_pin),
// pin_inv_en_(inv_pin),
// pin_inv_24V_en_(inv_24V_pin)
// {
// // Set pin mode
// pinMode(pin_brake_light_ctrl_, OUTPUT);
// pinMode(pin_inv_en_, OUTPUT);
// pinMode(pin_inv_24V_en_, OUTPUT);
// };
// MCUInterface(message_queue *msg_output_queue):
// MCUInterface(msg_output_queue, DEFAULT_BMS_OK_READ, DEFAULT_IMD_OK_READ, DEFAULT_BSPD_OK_READ, DEFAULT_SOFTWARE_OK_READ, DEFAULT_BOTS_OK_READ, DEFAULT_BRAKE_LIGHT_CTRL, DEFAULT_INVERTER_EN, DEFAULT_INVERTER_24V_EN) {};

/* Initialize shutdown circuit input readings */
void init();
Expand Down
2 changes: 1 addition & 1 deletion lib/interfaces/include/MessageQueueDefine.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
#include "FlexCAN_T4.h"

constexpr std::size_t CANBufferSize = 16;
using CANBufferType = CircularBuffer<uint8_t, CANBufferSize>;
using CANBufferType = Circular_Buffer<uint8_t, CANBufferSize>;

#endif
9 changes: 5 additions & 4 deletions lib/interfaces/include/TelemetryInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "FlexCAN_T4.h"
#include "HyTech_CAN.h"
#include "ht_can.h"
#include "hytech.h"
#include "SysClock.h"
#include "AnalogSensorsInterface.h"
#include "SteeringEncoderInterface.h"
Expand All @@ -18,11 +18,12 @@ class TelemetryInterface
MCU_rear_potentiometers mcu_rear_potentiometers_;
MCU_analog_readings mcu_analog_readings_;
/* CAN Tx buffer */
CANBufferType *msg_queue_;
// CANBufferType *msg_queue_;

public:
TelemetryInterface(message_queue *msg_output_queue):
msg_queue_(msg_output_queue) {};
TelemetryInterface(){};
// TelemetryInterface(message_queue *msg_output_queue):
// msg_queue_(msg_output_queue) {};

/* Update CAN messages (main loop) */
// Interfaces
Expand Down
Loading

0 comments on commit 31ffd77

Please sign in to comment.