Sports Event Timing' demonstrates the simulation of a race among dummy participants, both server-sided and client-sided.
The simulation is a choice that can be run either solely on the client-side, concurrently using reactjs redux-sagas OR entirely on the server side where reactjs ensures to update the client-side accordingly.
Every athlete carries a chip with a unique identifier. There are two set time points in the race, both of which also carrying unique chip identifiers, individually trigger a signal to the server side, indicating the athlete has reached said time point.
In case of the client side simulation, the client fetches dummy race participants, and dummy time points. Concurrently, the sagas then simulate the athletes running on the track, where whenever any given athlete trips over a given time point, the following information is sent to the server:
- Athlete Chip Identifier
- Time taken for the athlete to reach that time point
- Time Point Chip Identifier
The server then acnkowledges the request using REST endpoints and then stores the information in the database.
On the other hand, for the server side simulation, the server executes a goroutine-based race among athletes, stores their data in the database and makes that data available for client side fetching via REST endpoints. The server also uses similar mechanics to detect athlete chip identifiers and time point chip identifiers.
1. Go development environment and;
2. NPM/Yarn;
3. GCC 64 bit for inbuilt sqlite3 database support;
preferrably: https://sourceforge.net/projects/mingw-w64/
On a Windows machine, project can be built using the build.bat file.
Subsequently, it will create a dist folder that will contain the production ReactJS files
as well as the backend binary. Please ensure 64 bit gcc
(required for sqlite3) is available at command prompt.
1. Clone the project in the src folder of your GOPATH
2. Change directory into the project root
3. Ensure that the project directory is called 'sports-event-timing'
and follows the following folder structure: 'GOPATH/src/sports-event-timing/source/backend'
3. Run ./build.bat
Alternatively, production files can also be generated separately(backend and frontend).
Again, please ensure 64 bit gcc
(required for sqlite3) is available at command prompt. Steps as follows:
1. Clone the project in your GOPATH or download the repository as zip and extract it in your GOPATH.
2. Ensure that the project directory is called 'sports-event-timing'
and follows the following folder structure: 'GOPATH/src/sports-event-timing/source/backend'
3. Change directory into the %project root%/source/backend folder.
4. Run go build to get the built executable.
5. Change directory into the %project root%/source/frontend folder.
6. Run 'npm install' or just 'yarn'(project uses yarn) to install dependencies.
7. Run 'npm run build' or 'yarn build' to generate production files that will be placed inside %project root%/dist/ folder.
8. Make sure the server executable is in the same folder as all other distribution files i. e. same folder as index.html
The backend uses the package httprouter as its main mux to server REST Endpoints.
By default, the server port is 8082
endpoint | method | description |
---|---|---|
/server/race-simulation/start |
GET | Starts of the Race Event |
/server/race-simulation/stop |
GET | Stops the Race Event |
/server/race-simulation/fetch/live-standings |
GET | Returns the list of athletes with their race-relevant attributes in a live race |
/server/race-simulation/fetch/last-standings |
GET | Returns the list of athletes with their race-relevant attributes from the last race whereby, a race is currently not live |
Start Race
Request:
curl 'http://localhost:8082/server/race-simulation/start'
Response:
{"responseMessage":"starting race","athletes":[{"startNumber":1,"fullName":"Manish Singh","identifier":"bbk15uu8auq2p50gu1i0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":2,"fullName":"Madhushree Singh","identifier":"bbk15uu8auq2p50gu1ig","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":3,"fullName":"Siim Kaspar Uustalu","identifier":"bbk15uu8auq2p50gu1j0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":4,"fullName":"Eliisabeth Käbin","identifier":"bbk15uu8auq2p50gu1jg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":5,"fullName":"Ahti Liin","identifier":"bbk15uu8auq2p50gu1k0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1}]}
Stop Race
Request:
curl 'http://localhost:8082/server/race-simulation/stop'
Response:
{"responseMessage":"stopping race"}
Get live Athlete values in a live Race
Request:
curl 'http://localhost:8082/server/race-simulation/fetch/live-standings'
Response:
{"responseMessage":"race is in process, showing live data","athletes":[{"startNumber":1,"fullName":"Manish Singh","identifier":"bbk15uu8auq2p50gu1i0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":2,"fullName":"Madhushree Singh","identifier":"bbk15uu8auq2p50gu1ig","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":3,"fullName":"Siim Kaspar Uustalu","identifier":"bbk15uu8auq2p50gu1j0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":4,"fullName":"Eliisabeth Käbin","identifier":"bbk15uu8auq2p50gu1jg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":5,"fullName":"Ahti Liin","identifier":"bbk15uu8auq2p50gu1k0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1}]}
Get Athlete values in a from the last Race
Request:
curl 'http://localhost:8082/server/race-simulation/fetch/last-standings'
Response:
{"responseMessage":"race is currently not in process, showing last race data","athletes":[{"startNumber":1,"fullName":"Manish Singh","identifier":"bbk15uu8auq2p50gu1i0","location":209,"timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":2,"fullName":"Madhushree Singh","identifier":"bbk15uu8auq2p50gu1ig","location":203,"timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":3,"fullName":"Siim Kaspar Uustalu","identifier":"bbk15uu8auq2p50gu1j0","location":226,"timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":4,"fullName":"Eliisabeth Käbin","identifier":"bbk15uu8auq2p50gu1jg","location":201,"timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":5,"fullName":"Ahti Liin","identifier":"bbk15uu8auq2p50gu1k0","location":201,"timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1}]}
endpoint | method | description |
---|---|---|
/client/race-simulation/start |
GET | Starts of the Race Event |
/client/race-simulation/stop |
GET | Stops the Race Event |
/client/race-simulation/fetch/current-standings |
GET | Returns the list of athletes with their race-relevant attributes in a live race |
/client/race-simulation/push/current-standings |
POST | Accepts athlete attributes which it then stores in the database in a live race |
Start Race
Request:
curl 'http://localhost:8082/client/race-simulation/start'
Response:
{"responseMessage":"starting race","athletes":[{"startNumber":1,"fullName":"Manish Singh","identifier":"bbk0q968auq2p50gu1b0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":2,"fullName":"Madhushree Singh","identifier":"bbk0q968auq2p50gu1bg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":3,"fullName":"Siim Kaspar Uustalu","identifier":"bbk0q968auq2p50gu1c0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":4,"fullName":"Eliisabeth Käbin","identifier":"bbk0q968auq2p50gu1cg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":5,"fullName":"Ahti Liin","identifier":"bbk0q968auq2p50gu1d0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1}],"timePoints":[{"name":"Corridor Timepoint","location":800,"identifier":"bbk0q968auq2p50gu1dg"},{"name":"Finish Line Timepoint","location":1000,"identifier":"bbk0q968auq2p50gu1e0"}]}
Stop Race
Request:
curl 'http://localhost:8082/client/race-simulation/stop'
Response:
{"responseMessage":"stopping race"}
Fetch Current Athletes in the Race
Request:
curl 'http://localhost:8082/client/race-simulation/fetch/current-standings'
Response:
{"responseMessage":"race is in process, showing live data","athletes":[{"startNumber":1,"fullName":"Manish Singh","identifier":"bbk15hu8auq2p50gu1eg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":2,"fullName":"Madhushree Singh","identifier":"bbk15hu8auq2p50gu1f0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":3,"fullName":"Siim Kaspar Uustalu","identifier":"bbk15hu8auq2p50gu1fg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":4,"fullName":"Eliisabeth Käbin","identifier":"bbk15hu8auq2p50gu1g0","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":5,"fullName":"Ahti Liin","identifier":"bbk15hu8auq2p50gu1gg","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1}]}
Push updated Athletes values from Client to Server
Request:
curl 'http://localhost:8082/client/race-simulation/push/current-standings' --data-binary '{"requestCommand":"commit feed","athletes":[{"timeElapsed":33614,"identifier":"bbk0ntm8auq2p50gu17g","timePointIdentifier":"bbk0ntm8auq2p50gu1a0"}]}'
Response:
{"responseMessage":"race is in process, data was committed, showing committed data","athletes":[{"startNumber":1,"fullName":"Manish Singh","identifier":"bbk0ntm8auq2p50gu17g","inFinishCorridor":true,"timeTakenToReachFinishCorridor":33614,"timeTakenToFinish":-1},{"startNumber":2,"fullName":"Madhushree Singh","identifier":"bbk0ntm8auq2p50gu180","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":3,"fullName":"Siim Kaspar Uustalu","identifier":"bbk0ntm8auq2p50gu18g","timeTakenToReachFinishCorridor":-1,"timeTakenToFinish":-1},{"startNumber":4,"fullName":"Eliisabeth Käbin","identifier":"bbk0ntm8auq2p50gu190","inFinishCorridor":true,"timeTakenToReachFinishCorridor":33485,"timeTakenToFinish":-1},{"startNumber":5,"fullName":"Ahti Liin","identifier":"bbk0ntm8auq2p50gu19g","inFinishCorridor":true,"timeTakenToReachFinishCorridor":33647,"timeTakenToFinish":-1}]}
- Notes:
Ideally, the response message should be in the format of 'success' or 'fail' and should use status codes more effectively to better adhere to REST EndPoint standards. The backend uses defensive analysis to ascertain the condition of the race event before responding to the client. The client side, might however, lack coping mechanisms to accurately reflect the backend's status responses. This is true at the time of writing.
1, May, 2018
The frontend is based on ReactJS, using Redux for state-management and Redux Saga as a side-effects middleware.
Bootstrap is used as the CSS framework of choice and SCSS is used to modify the theme.
Click here to see the component diagram of the application structure.
- Notes:
CSS modules in combination with SCSS files that allow for global CSS/SCSS dependencies(or from node_modules) to stay intact would have been a much proficient set up, not found in this project. Usage of FlipMove external dependency seems to be bad with performance, however, ideal for this project, compared to my slide animation attempt using react-transition-group, but definitely something to retry.
1, May, 2018
The current database used is sqlite 3. For the database, GORM is used as an ORM to allow quick replacement of database software without requiring to re-write code.
These are the following tables created:
athlete_db_models
contains columns:
full_name string
start_number int
chip_identifier string
in_finish_corridor boolean
has_finished boolean
location int
time_taken_to_reach_finish_corridor int
time_taken_to_finish int
timepoint_db_models
contains columns:
name string
location int
chip_identifier string
The database file itself(called 'RaceEventDB.db') is located in the 'dist' folder if using production build.
- Thorough Unit-Testing.
- Replace mutexes in backend with pure channel-based solution.
- Stories on frontend.
- httprouter - The backend router used
- react - The frontend javascript library used
- redux - State Management
- redux-saga - To handle side-effects and other asynchronous tasks
- sqlite3 - Relational drop-in database for quick demo
- Manish Singh - Initial work - kryptodev
This project is licensed under The Unlicence License - see the LICENSE file for details