+ );
+};
diff --git a/src/components/Header/logo.png b/src/components/Header/logo.png
new file mode 100644
index 0000000..500b845
Binary files /dev/null and b/src/components/Header/logo.png differ
diff --git a/src/components/Ticker/Ticker.sass b/src/components/Ticker/Ticker.sass
new file mode 100644
index 0000000..efe4761
--- /dev/null
+++ b/src/components/Ticker/Ticker.sass
@@ -0,0 +1,12 @@
+.ticker-container
+ text-align: center
+ margin: 3rem auto 1rem auto
+ h1
+ margin-bottom: 2rem
+em
+ display: block
+ max-width: 35rem
+ margin: 2rem auto 1rem auto
+ font-size: 12pt
+ text-decoration: none
+ color: initial
diff --git a/src/components/Ticker/Ticker.tsx b/src/components/Ticker/Ticker.tsx
new file mode 100644
index 0000000..ab1dd9e
--- /dev/null
+++ b/src/components/Ticker/Ticker.tsx
@@ -0,0 +1,32 @@
+/* global pywebview */
+
+import * as React from 'react'
+
+import './Ticker.sass'
+import logo from '../../assets/logo.png'
+
+
+export default function Ticker() {
+ const [ticker, setTicker] = React.useState('')
+
+ React.useEffect(() => {
+ window.addEventListener('pywebviewready', function () {
+
+ if (!(window as any).pywebview.state) {
+ (window as any).pywebview.state = {}
+ }
+ // Expose setTicker in order to call it from Python
+ (window as any).pywebview.state.setTicker = setTicker
+ })
+ }, [])
+
+ return (
+
+
Welcome to pywebview
+
+
+ You can freely communicate between Javascript with Python without an HTTP server. This value, for example, is being generated by Python code:
+ {ticker}
+
+ )
+}
diff --git a/src/index.jsx b/src/index.jsx
new file mode 100644
index 0000000..be9f4dd
--- /dev/null
+++ b/src/index.jsx
@@ -0,0 +1,31 @@
+
+import ReactDOM from 'react-dom'
+import reportWebVitals from './reportWebVitals';
+
+import Header from './components/Header/Header'
+import Editor from './components/Editor/Editor'
+import Ticker from './components/Ticker/Ticker'
+
+import './index.sass'
+
+const App = function () {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+const view = App('pywebview')
+
+const element = document.getElementById('app')
+ReactDOM.render(view, element)
+
+
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
\ No newline at end of file
diff --git a/src/index.py b/src/index.py
new file mode 100644
index 0000000..e14eca5
--- /dev/null
+++ b/src/index.py
@@ -0,0 +1,63 @@
+import os
+import threading
+import webview
+
+from time import time
+from lib.serialhandler import SerialHandler, DataFrame, FormatData, FileHandler
+from lib.api import Api
+
+
+def get_entrypoint():
+
+ def exists(path):
+ return os.path.exists(os.path.join(os.path.dirname(__file__), path))
+
+ if exists('../gui/index.html'): # unfrozen development
+ return '../gui/index.html'
+
+ if exists('../Resources/gui/index.html'): # frozen py2app
+ return '../Resources/gui/index.html'
+
+ if exists('./gui/index.html'):
+ return './gui/index.html'
+
+ raise Exception('No index.html found')
+
+
+def set_interval(interval):
+
+ def decorator(function):
+
+ def wrapper(*args, **kwargs):
+ stopped = threading.Event()
+
+ def loop(): # executed in another thread
+ while not stopped.wait(interval): # until stopped
+ function(*args, **kwargs)
+
+ t = threading.Thread(target=loop)
+ t.daemon = True # stop if the program exits
+ t.start()
+ return stopped
+
+ return wrapper
+
+ return decorator
+
+
+entry = get_entrypoint()
+
+
+@set_interval(1)
+def update_ticker():
+ if len(webview.windows) > 0:
+ webview.windows[0].evaluate_js(
+ 'window.pywebview.state.setTicker("%d")' % time())
+
+
+if __name__ == '__main__':
+
+ window = webview.create_window('pywebview-react boilerplate',
+ entry,
+ js_api=Api())
+ webview.start(update_ticker, debug=True)
diff --git a/src/index.sass b/src/index.sass
new file mode 100644
index 0000000..0ea7211
--- /dev/null
+++ b/src/index.sass
@@ -0,0 +1,15 @@
+@import './styles/fonts'
+body
+ margin: 0
+ padding: 0
+ font-family: 'Open Sans', 'Helvetica Neue', sans-serif
+ font-size: 14pt
+ line-height: 130%
+ color: #2c3e50
+ background-color: white
+textarea
+ font-family: 'Open Sans', 'Helvetica Neue', sans-serif
+ font-size: 14pt
+ padding: 0.6rem
+h1, h2, h3
+ font-family: 'Roboto Condensed', 'Helvetica Neue', sans-serif
diff --git a/src/lib/api/__init__.py b/src/lib/api/__init__.py
new file mode 100644
index 0000000..bb7f9ff
--- /dev/null
+++ b/src/lib/api/__init__.py
@@ -0,0 +1,16 @@
+from serial.tools import list_ports
+
+
+class Api:
+
+ def get_all_ports(self):
+ ports = list_ports.comports()
+ res = []
+
+ for port in ports:
+ res.append(port.device)
+
+ return res
+
+ def test(self):
+ raise NotImplementedError()
diff --git a/src/lib/serialhandler/DataFrame.py b/src/lib/serialhandler/DataFrame.py
new file mode 100644
index 0000000..6d16ee4
--- /dev/null
+++ b/src/lib/serialhandler/DataFrame.py
@@ -0,0 +1,141 @@
+class DataFrame:
+
+ __doc__ = '''Documentation for DataFrame.py instance functions:
+
+-getEngineFrame():
+ returns the dictionary with engine sensors values
+
+-getGPSFrame():
+ returns the dictionary with GPS values
+
+-getWheelSensorsFrame():
+ returns the dictionary with proprietary sensors values
+
+-getGyroScopeFrame():
+ returns the dictionary with the gyroscope (and the accelerometer) sensor values
+
+-setEngineFrame(key, value):
+ sets the value associated to the engineFrame's key given as attribute
+
+-setGPSFrame(key, value):
+ sets the value associated to the GPSFrame's key given as attribute
+ returns:
+ True if it is successful
+ False if the key given as attribute doesn't exist
+
+-setWheelSensorsFrame(key, value):
+ sets the value associated to the WheelSensorsFrame's key given as attribute
+ returns:
+ True if it is successful
+ False if the key given as attribute doesn't exist
+
+-setGyroscopeFrame(key, value):
+ sets the value associated to the WheelSensorsFrame's key given as attribute
+ returns:
+ True if it is successful
+ False if the key given as attribute doesn't exist
+
+Since all the data structures are dictionaries, to see the list of keys, call the keys() function on the dictionary'''
+
+ def __init__(self):
+ # DEFINITION OF DICTIONARIES' FIELDS
+ self.__engineFrame = {
+ "rpm": 0,
+ "tps": 0.0,
+ "t_h20": 0,
+ "t_air": 0,
+ "t_oil": 0,
+ "vbb": 0.0,
+ "lambda1_avg": 0.0,
+ "lambda1_raw": 0.0,
+ "k_lambda1": 0.0,
+ "inj_low": 0.0,
+ "inj_high": 0.0,
+ "gear": 0
+ }
+ self.__GPSFrame = {
+ "hour": 0,
+ "minutes": 0,
+ "seconds": 0,
+ "micro_seconds": 0.0,
+ "n_s": "",
+ "e_w": "",
+ "fixQuality": 0,
+ "n_sats": 0,
+ "hdop": 0.0,
+ "latitude": 0.0,
+ "longitude": 0.0,
+ "velGPS": 0.0
+ }
+ self.__wheelSensorsFrame = {
+ "countFSx": 0,
+ "countFDx": 0,
+ "dtF": 0,
+ "countRSx": 0,
+ "countRDx": 0,
+ "dtR": 0,
+ "vel_fsx": 0.0,
+ "vel_fdx": 0.0,
+ "vel_rdx": 0.0,
+ "vel_rsx": 0.0,
+ "pot_fsx": 0.0,
+ "pot_fdx": 0.0,
+ "pot_rdx": 0.0,
+ "pot_rsx": 0.0,
+ "potRAccuracy": 0.0,
+ "potFAccuracy": 0.0,
+ "steeringEncoder": 0.0
+ }
+ self.__gyroscopeFrame = {
+ "gyro_x": 0.0,
+ "gyro_y": 0.0,
+ "gyro_z": 0.0,
+ "accel_x": 0.0,
+ "accel_y": 0.0,
+ "accel_z": 0.0,
+ }
+
+ # GETTING DICTIONARIES FUNCTIONS
+ def getEngineFrame(self):
+ return (self.__engineFrame)
+
+ def getGPSFrame(self):
+ return (self.__GPSFrame)
+
+ def getWheelSensorsFrame(self):
+ return (self.__wheelSensorsFrame)
+
+ def getGyroscopeFrame(self):
+ return (self.__gyroscopeFrame)
+
+ # SETTING DICTIONARIES FUNCTIONS
+ def setEngineFrame(self, key, value):
+ if key in self.__engineFrame.keys():
+ self.__engineFrame[key] = value
+ return True
+ else:
+ return False
+
+ def setGPSFrame(self, key, value):
+ if key in self.__GPSFrame.keys():
+ self.__GPSFrame[key] = value
+ return True
+ else:
+ return False
+
+ def setWheelSensorsFrame(self, key, value):
+ if key in self.__wheelSensorsFrame.keys():
+ self.__wheelSensorsFrame[key] = value
+ return True
+ else:
+ return False
+
+ def setGyroscopeFrame(self, key, value):
+ if key in self.__gyroscopeFrame.keys():
+ self.__gyroscopeFrame[key] = value
+ return True
+ else:
+ return False
+
+ def __repr__(self):
+ return f"engine: {self.__engineFrame} - gyro: {self.__gyroscopeFrame} - wheel: {self.__wheelSensorsFrame} - GPS: {self.__GPSFrame}"
diff --git a/src/lib/serialhandler/FileHandler.py b/src/lib/serialhandler/FileHandler.py
new file mode 100644
index 0000000..30e1ee0
--- /dev/null
+++ b/src/lib/serialhandler/FileHandler.py
@@ -0,0 +1,227 @@
+import csv
+import time
+# from serialdata import DataFrame
+
+
+class FileHandler:
+
+ __doc__ = '''Documentation for FormatData.py functions:
+
+ __init__(self, dataFrame):
+ creates a fileHandler object to save data in a CSV file called "dd_mm_yyyy hh_mm__ss.csv"
+ the fileHandler object has a dataFrame variable where the dataFrame is saved
+ it opens the three files in order to have them ready to be written
+
+ write100Hz():
+ writes new 100Hz data inside the .csv file.
+ In order to save data carefully it closes the file every 500 writings (5 seconds)
+
+ write10Hz():
+ writes new 10Hz data inside the .csv file.
+ In order to save data carefully it closes the file every 50 writings (5 seconds)
+
+ write4Hz():
+ writes new 4Hz data inside the .csv file.
+ In order to save data carefully it closes the file every 20 writings (5 seconds)
+
+ writeReadError():
+ used in formatData.setData() to fill the .csv files' fields with 'ReadError'.
+ Since we do not know which block is transmitting, a line full of 'ReadError' will be added to every file (100Hz, 10Hz, 4Hz)
+ '''
+
+ # Creates a FileHandler object to save data in a CSV file called "dd_mm_yyyy_hh_mm_ss.csv"
+
+ def __init__(self, dataFrame):
+ self.__dataFrame = dataFrame
+ nome = time.strftime("%a_%d_%b_%Y") + "_" + time.strftime("%H_%M_%S_")
+ self.__name100Hz = nome + "100Hz.csv"
+ self.__name10Hz = nome + "10Hz.csv"
+ self.__name4Hz = nome + "4Hz.csv"
+
+ # Getting dictionaries from a dataFrame object
+ self.__engineFrame = self.__dataFrame.getEngineFrame()
+ self.__wheelFrame = self.__dataFrame.getWheelSensorsFrame()
+ self.__gyroscopeFrame = self.__dataFrame.getGyroscopeFrame()
+ self.__GPSFrame = self.__dataFrame.getGPSFrame()
+
+ # Dictionaries used to write data inside the .csv file (sorted by frequency)
+ self.__FrameValues100Hz = {
+ 'rpm': 0,
+ 'tps': 0.0,
+ 'accel_x': 0.0,
+ 'accel_y': 0.0,
+ 'accel_z': 0.0,
+ 'gyro_x': 0.0,
+ 'gyro_y': 0.0,
+ 'gyro_z': 0.0,
+ 'pot_fsx': 0.0,
+ 'pot_fdx': 0.0,
+ 'pot_FAccuracy': 0.0,
+ 'pot_rsx': 0.0,
+ 'pot_rdx': 0.0,
+ 'pot_RAccuracy': 0.0,
+ 'steeringEncoder': 0.0,
+ 'vel_fsx': 0.0,
+ 'vel_fdx': 0.0,
+ 'vel_rsx': 0.0,
+ 'vel_rdx': 0.0,
+ 'gear': 0
+ }
+ self.__FrameValues10Hz = {
+ 't_h20': 0,
+ 't_air': 0,
+ 't_oil': 0,
+ 'vbb': 0.0,
+ 'lambda1_avg': 0.0,
+ 'lambda1_raw': 0.0,
+ 'k_lambda1': 0.0,
+ 'inj_low': 0.0,
+ 'inj_high': 0.0
+ }
+ self.__FrameValues4Hz = {
+ 'hour': 0,
+ 'minutes': 0,
+ 'seconds': 0,
+ 'micro_seconds': 0.0,
+ 'n_sats': 0,
+ 'fixQuality': 0,
+ 'e_w': "",
+ 'n_s': "",
+ 'hdop': 0.0,
+ 'latitude': 0.0,
+ 'longitude': 0.0,
+ 'velGPS': 0.0
+ }
+
+ # LINES WRITTEN (USED TO KNOW THE FILE HAS TO BE CLOSED AND REOPENED)
+ self.__lineNumber100Hz = 0
+ self.__lineNumber10Hz = 0
+ self.__lineNumber4Hz = 0
+
+ # CREATION OF THE dd_mm_yyyy_hh_mm_ss_100Hz.csv FILE
+ self.__file100Hz = open(self.__name100Hz, 'w', newline='')
+ self.__writerFile100Hz = csv.writer(self.__file100Hz,
+ delimiter=';',
+ dialect='excel')
+ self.__writerFile100Hz.writerow(list(self.__FrameValues100Hz.keys()))
+
+ # CREATION OF THE dd_mm_yyyy_hh_mm_ss_10Hz.csv FILE
+ self.__file10Hz = open(self.__name10Hz, 'w', newline='')
+ self.__writerFile10Hz = csv.writer(self.__file10Hz,
+ delimiter=';',
+ dialect='excel')
+ self.__writerFile10Hz.writerow(list(self.__FrameValues10Hz.keys()))
+
+ # CREATION OF THE dd_mm_yyyy_hh_mm_ss_4Hz.csv FILE
+ self.__file4Hz = open(self.__name4Hz, 'w', newline='')
+ self.__writerFile4Hz = csv.writer(self.__file4Hz,
+ delimiter=';',
+ dialect='excel')
+ self.__writerFile4Hz.writerow(list(self.__FrameValues4Hz.keys()))
+
+
+# Appends data to the file created before
+
+ def write100Hz(self):
+ # getting the updated dictionaries from the dataFrame object
+ self.__engineFrame = self.__dataFrame.getEngineFrame()
+ self.__wheelFrame = self.__dataFrame.getWheelSensorsFrame()
+ self.__gyroscopeFrame = self.__dataFrame.getGyroscopeFrame()
+
+ # Updating the "writing" dictionary with new values (a loop isn't used because three different frames are needed)
+ self.__FrameValues100Hz["rpm"] = self.__engineFrame["rpm"]
+ self.__FrameValues100Hz["tps"] = self.__engineFrame["tps"]
+ self.__FrameValues100Hz['accel_x'] = self.__gyroscopeFrame['accel_x']
+ self.__FrameValues100Hz['accel_y'] = self.__gyroscopeFrame['accel_y']
+ self.__FrameValues100Hz['accel_z'] = self.__gyroscopeFrame['accel_z']
+ self.__FrameValues100Hz['gyro_x'] = self.__gyroscopeFrame['gyro_x']
+ self.__FrameValues100Hz['gyro_y'] = self.__gyroscopeFrame['gyro_y']
+ self.__FrameValues100Hz['gyro_z'] = self.__gyroscopeFrame['gyro_z']
+ self.__FrameValues100Hz['pot_fsx'] = self.__wheelFrame['pot_fsx']
+ self.__FrameValues100Hz['pot_fdx'] = self.__wheelFrame['pot_fdx']
+ self.__FrameValues100Hz['potFAccuracy'] = self.__wheelFrame[
+ 'potFAccuracy']
+ self.__FrameValues100Hz['pot_rsx'] = self.__wheelFrame['pot_rsx']
+ self.__FrameValues100Hz['pot_rdx'] = self.__wheelFrame['pot_rdx']
+ self.__FrameValues100Hz['potRAccuracy'] = self.__wheelFrame[
+ 'potRAccuracy']
+ self.__FrameValues100Hz['steeringEncoder'] = self.__wheelFrame[
+ 'steeringEncoder']
+ self.__FrameValues100Hz['vel_fsx'] = self.__wheelFrame['vel_fsx']
+ self.__FrameValues100Hz['vel_fdx'] = self.__wheelFrame['vel_fdx']
+ self.__FrameValues100Hz['vel_rsx'] = self.__wheelFrame['vel_rsx']
+ self.__FrameValues100Hz['vel_rdx'] = self.__wheelFrame['vel_rdx']
+ # self.__FrameValues100Hz['gear'] = self.__wheelFrame['gear']
+
+ # Writing the whole line in dd_mm_yyyy_hh_mm_ss_100Hz.csv file
+ self.__writerFile100Hz.writerow(list(self.__FrameValues100Hz.values()))
+
+ # CLOSES THE FILE EVERY 500 WRITINGS
+ self.__lineNumber100Hz = (self.__lineNumber100Hz + 1) % 500
+ if (self.__lineNumber100Hz == 0):
+ self.__file100Hz.close()
+ self.__file100Hz = open(self.__name100Hz, 'a', newline='')
+ self.__writerFile100Hz = csv.writer(self.__file100Hz,
+ delimiter=';',
+ dialect='excel')
+
+ def write10Hz(self):
+ # getting the updated dictionariy from the dataFrame object (only engineFrame is needed)
+ self.__engineFrame = self.__dataFrame.getEngineFrame()
+
+ # Updating the "writing" dictionary
+ for key in self.__FrameValues10Hz.keys():
+ self.__FrameValues10Hz[key] = self.__engineFrame[key]
+
+ # Writing the whole line in dd_mm_yyyy_hh_mm_ss_10Hz.csv
+ self.__writerFile10Hz.writerow(list(self.__FrameValues10Hz.values()))
+
+ # CLOSES THE FILE EVERY 50 WRITINGS
+ self.__lineNumber10Hz = (self.__lineNumber10Hz + 1) % 50
+ if (self.__lineNumber10Hz == 0):
+ self.__file10Hz.close()
+ self.__file10Hz = open(self.__name10Hz, 'a', newline='')
+ self.__writerFile10Hz = csv.writer(self.__file10Hz,
+ delimiter=';',
+ dialect='excel')
+
+ def write4Hz(self):
+ # getting the updated dictionariy from the dataFrame object (only GPSFrame is needed)
+ self.__GPSFrame = self.__dataFrame.getGPSFrame()
+
+ # Updating the "writing" dictionary
+ for key in self.__FrameValues4Hz.keys():
+ self.__FrameValues4Hz[key] = self.__GPSFrame[key]
+
+ # Writing the whole line in dd_mm_yyyy_hh_mm_ss_4Hz.csv
+ self.__writerFile4Hz.writerow(list(self.__FrameValues4Hz.values()))
+
+ # CLOSES THE FILE EVERY 20 WRITINGS
+ self.__lineNumber4Hz = (self.__lineNumber4Hz + 1) % 20
+ if (self.__lineNumber4Hz == 0):
+ self.__file4Hz.close()
+ self.__file4Hz = open(self.__name4Hz, 'a', newline='')
+ self.__writerFile4Hz = csv.writer(self.__file4Hz,
+ delimiter=';',
+ dialect='excel')
+
+ # if the headerIndex has been received writes "ReadError" in the whole line of the file. If not received, "ReadError" will be written in the last line of each file
+ def writeReadError(self, *args):
+ if len(args) > 0:
+ if args[0] == 0x3F:
+ self.__writerFile100Hz.writerow(
+ ['ReadError'] * len(self.__FrameValues100Hz.keys()))
+ elif args[0] == 0x0A:
+ self.__writerFile10Hz.writerow(
+ ['ReadError'] * len(self.__FrameValues10Hz.keys()))
+ elif args[0] == 0x04:
+ self.__writerFile4Hz.writerow(
+ ['ReadError'] * len(self.__FrameValues4Hz.keys()))
+
+ else:
+ self.__writerFile100Hz.writerow(
+ ['ReadError'] * len(self.__FrameValues100Hz.keys()))
+ self.__writerFile10Hz.writerow(['ReadError'] *
+ len(self.__FrameValues10Hz.keys()))
+ self.__writerFile4Hz.writerow(['ReadError'] *
+ len(self.__FrameValues4Hz.keys()))
diff --git a/src/lib/serialhandler/FormatData.py b/src/lib/serialhandler/FormatData.py
new file mode 100644
index 0000000..88fd3f4
--- /dev/null
+++ b/src/lib/serialhandler/FormatData.py
@@ -0,0 +1,218 @@
+# from serialdata import FileHandler
+
+
+class FormatData:
+
+ __doc__ = '''Documentation for FormatData.py functions:
+
+ @classmethod
+ formatData(encodedMessage):
+ it decodes the encodedMessage given as attribute to the function
+ if the encodedMessage had been received without errors (the pattern is: every four bytes only three are data bytes):
+ returns: a list composed by the headerIndex (to know which block (100Hz, 10Hz, 4Hz) the message belongs to) and the data bytes
+
+ else:
+ returns: ['E', 'R', 'R', 'O', 'R'] to emphasize that an error has occured
+
+ @classmethod
+ setData(dataFrame, encodedMessage, fileHandler):
+ using formatData() function, it decodes the message and updates the dictionaries inside the dataFrame object.
+ if (decodedMessage[0]==0x3F): checks if the transmitted block is the 100Hz one
+ elif (decodedMessage[0]==0x0A): checks if the transmitted block is the 10Hz one
+ elif(decodedMessage[0]==0x04): checks if the transmitted block is the 4Hz one
+
+ Once the update is completed, the function uses the fileHandler object to save data to .csv file
+ '''
+
+ @classmethod
+ def formatData(cls, encodedMessage):
+ if encodedMessage in [
+ bytes([0x3F]) + b'ReadError',
+ bytes([0x0A]) + b'ReadError',
+ bytes([0x0A]) + b'ReadError'
+ ]:
+ return ['E', 'R', 'R', 'O', 'R', encodedMessage[0] & 0x3F]
+
+ elif len(encodedMessage) % 4 != 1 or (
+ encodedMessage == b'ReadError'
+ ): # We receive a number of bytes multiple of 4, plus the byte which gives us the headerIndex of the block received
+ return [
+ 'E', 'R', 'R', 'O', 'R'
+ ] # if it is False (it includes also the case of receiving 'ReadError') returns ['E', 'R', 'R', 'O', 'R'] to emphasize that an error has occured
+
+ else:
+ encodedMessage = list(encodedMessage)
+ decodedMessage = []
+ headerIndex = encodedMessage[0] & 0x3F
+ decodedMessage.append(headerIndex)
+ for i in range(0, len(encodedMessage) // 4):
+ firstByte = ((encodedMessage[4 * i + 1] & 0x3F) << 2 |
+ (encodedMessage[4 * i + 2] & 0x3F) >> 4) & 0xFF
+ secondByte = ((encodedMessage[4 * i + 2] & 0x3F) << 4 |
+ (encodedMessage[4 * i + 3] & 0x3F) >> 2) & 0xFF
+ thirdByte = ((encodedMessage[4 * i + 3] & 0x3F) << 6 |
+ (encodedMessage[4 * i + 4] & 0x3F)) & 0xFF
+ decodedMessage.append(firstByte)
+ decodedMessage.append(secondByte)
+ decodedMessage.append(thirdByte)
+ return decodedMessage
+
+ @classmethod
+ def setData(cls, dataFrame, encodedMessage, fileHandler):
+ decodedMessage = FormatData.formatData(encodedMessage)
+ if (decodedMessage[0] == 0x3F):
+ dataFrame.setEngineFrame(
+ "rpm",
+ int(decodedMessage[1]) << 8 | int(decodedMessage[2]))
+ dataFrame.setEngineFrame(
+ "tps",
+ (float(int(decodedMessage[3]) << 8 | int(decodedMessage[4]))) /
+ 10)
+
+ dataFrame.setGyroscopeFrame(
+ "accel_x",
+ (float(int(decodedMessage[5]) << 8 | int(decodedMessage[6]))) /
+ 8192)
+ dataFrame.setGyroscopeFrame(
+ "accel_y",
+ (float(int(decodedMessage[7]) << 8 | int(decodedMessage[8]))) /
+ 8192)
+ dataFrame.setGyroscopeFrame(
+ "accel_z",
+ (float(int(decodedMessage[9]) << 8 | int(decodedMessage[10])))
+ / 8192)
+
+ dataFrame.setGyroscopeFrame(
+ "gyro_x",
+ (float(int(decodedMessage[11]) << 8 | int(decodedMessage[12])))
+ / 8192)
+ dataFrame.setGyroscopeFrame(
+ "gyro_y",
+ (float(int(decodedMessage[13]) << 8 | int(decodedMessage[14])))
+ / 8192)
+ dataFrame.setGyroscopeFrame(
+ "gyro_z",
+ (float(int(decodedMessage[15]) << 8 | int(decodedMessage[16])))
+ / 8192)
+
+ # POTENTIOMETER'S OFFSET (THE NUMBER SUBTRACTED AT THE END OF THE LINE) MUST BE CALCULATED AT EVERY CAR'S TEST
+ dataFrame.setWheelSensorsFrame(
+ "pot_fsx", (int(decodedMessage[17]) |
+ ((int(decodedMessage[19] & 0x0F)) << 8)) - 610)
+ dataFrame.setWheelSensorsFrame(
+ "pot_fdx", (int(decodedMessage[18]) |
+ ((int(decodedMessage[19] & 0xF0)) << 4)) - 410)
+ dataFrame.setWheelSensorsFrame("potFAccuracy",
+ int(decodedMessage[20]))
+ dataFrame.setWheelSensorsFrame(
+ "pot_rsx", (int(decodedMessage[21]) |
+ ((int(decodedMessage[23] & 0x0F)) << 8)) - 300)
+ dataFrame.setWheelSensorsFrame(
+ "pot_rdx", (int(decodedMessage[22]) |
+ ((int(decodedMessage[23] & 0xF0)) << 4)) - 310)
+ dataFrame.setWheelSensorsFrame("potRAccuracy",
+ int(decodedMessage[24]))
+
+ dataFrame.setWheelSensorsFrame(
+ "countFSx", (int(decodedMessage[27] & 0x0F) << 8)
+ | int(decodedMessage[25]))
+ dataFrame.setWheelSensorsFrame(
+ "countFDx", (int(decodedMessage[27] & 0xF0) << 4)
+ | int(decodedMessage[26]))
+ dataFrame.setWheelSensorsFrame("dtF", int(decodedMessage[28]))
+
+ # dataFrame.setWheelSensorsFrame("vel_fsx", SPEED_VALUE) # NOT IN USE (PHONIC WHEEL'S SPECS NOT AVAILABLE)
+ # dataFrame.setWheelSensorsFrame("vel_fdx", SPEED_VALUE) # NOT IN USE (PHONIC WHEEL'S SPECS NOT AVAILABLE)
+
+ dataFrame.setWheelSensorsFrame(
+ "countRSx", (int(decodedMessage[32] & 0x0F) << 8)
+ | int(decodedMessage[30]))
+ dataFrame.setWheelSensorsFrame(
+ "countRDx", (int(decodedMessage[32] & 0xF0) << 4)
+ | int(decodedMessage[31]))
+ dataFrame.setWheelSensorsFrame("dtR", int(decodedMessage[33]))
+
+ # dataFrame.setWheelSensorsFrame("vel_rsx", SPEED_VALUE) # NOT IN USE (PHONIC WHEEL'S SPECS NOT AVAILABLE)
+ # dataFrame.setWheelSensorsFrame("vel_rdx", SPEED_VALUE) # NOT IN USE (PHONIC WHEEL'S SPECS NOT AVAILABLE)
+
+ dataFrame.setEngineFrame("gear", int(decodedMessage[34]))
+
+ fileHandler.write100Hz()
+
+ elif (decodedMessage[0] == 0x0A):
+ dataFrame.setEngineFrame("t_h20", int(decodedMessage[1]) - 40)
+ dataFrame.setEngineFrame("t_air", int(decodedMessage[2]) - 40)
+ dataFrame.setEngineFrame("t_oil", int(decodedMessage[3]) - 40)
+ dataFrame.setEngineFrame("vbb",
+ (float(int(decodedMessage[4]))) * 0.0705)
+ dataFrame.setEngineFrame("lambda1_avg",
+ (float(int(decodedMessage[5]))) / 100)
+ dataFrame.setEngineFrame("lambda1_raw",
+ (float(int(decodedMessage[6]))) / 100)
+ dataFrame.setEngineFrame(
+ "k_lambda1",
+ (float(int(decodedMessage[7]) << 8 | int(decodedMessage[8]))) /
+ 656)
+ dataFrame.setEngineFrame(
+ "inj_low",
+ (float(int(decodedMessage[9]) << 8 | int(decodedMessage[10])))
+ / 2)
+ dataFrame.setEngineFrame(
+ "inj_high",
+ (float(int(decodedMessage[11]) << 8 | int(decodedMessage[12])))
+ / 2)
+
+ fileHandler.write10Hz()
+
+ elif (decodedMessage[0] == 0x04):
+ dataFrame.setGPSFrame("hour", int(decodedMessage[1]))
+ dataFrame.setGPSFrame("minutes", int(decodedMessage[2]))
+ dataFrame.setGPSFrame("seconds", int(decodedMessage[3]))
+ dataFrame.setGPSFrame("micro_seconds",
+ (int(decodedMessage[4]) << 8) |
+ (int(decodedMessage[5])))
+ dataFrame.setGPSFrame("n_sats", int(decodedMessage[6] & 0x0F))
+ dataFrame.setGPSFrame("fixQuality",
+ int((decodedMessage[6] & 0x30) >> 4))
+ e_w = int((decodedMessage[6] >> 6) & 0x01)
+ if (e_w == 1):
+ dataFrame.setGPSFrame("e_w", "E")
+ else:
+ dataFrame.setGPSFrame("e_w", "W")
+
+ n_s = int(decodedMessage[6] >> 7)
+ if (n_s == 1):
+ dataFrame.setGPSFrame("n_s", "N")
+ else:
+ dataFrame.setGPSFrame("n_s", "S")
+
+ dataFrame.setGPSFrame(
+ "hdop",
+ int(decodedMessage[7] << 8) | int(decodedMessage[8]))
+ dataFrame.setGPSFrame(
+ "latitude",
+ ((int(decodedMessage[9]) << 8) | int(decodedMessage[10])) +
+ ((float((int(decodedMessage[11]) << 24) |
+ (int(decodedMessage[12]) << 16) |
+ (int(decodedMessage[13]) << 8)
+ | int(decodedMessage[14]))) / 100000))
+ dataFrame.setGPSFrame(
+ "longitude",
+ ((int(decodedMessage[15]) << 24) |
+ (int(decodedMessage[16]) << 16) |
+ (int(decodedMessage[17]) << 8) | int(decodedMessage[18])) +
+ ((float((int(decodedMessage[19]) << 24) |
+ (int(decodedMessage[20]) << 16) |
+ (int(decodedMessage[21]) << 8)
+ | int(decodedMessage[22]))) / 100000))
+ dataFrame.setGPSFrame(
+ "velGPS",
+ float(decodedMessage[23]) + ((float(decodedMessage[23])) / 10))
+
+ fileHandler.write4Hz()
+
+ elif decodedMessage[0:5] == ['E', 'R', 'R', 'O', 'R']:
+ if len(decodedMessage) == 6:
+ fileHandler.writeReadError(decodedMessage[5])
+ else:
+ fileHandler.writeReadError()
diff --git a/src/lib/serialhandler/OldFileHandler.py b/src/lib/serialhandler/OldFileHandler.py
new file mode 100644
index 0000000..8c12e7d
--- /dev/null
+++ b/src/lib/serialhandler/OldFileHandler.py
@@ -0,0 +1,75 @@
+import csv
+import time
+# from GUI.RealTime import DataFrame
+
+
+class FileHandler:
+
+ # Creates a FileHandler object to save data in CSV file called "dd_mm_yyyy hh_mm__ss.csv"
+
+ def __init__(self, dataFrame):
+ self.__dataFrame = dataFrame
+ nome = time.strftime("%a_%d_%b_%Y") + " " + time.strftime("%H_%M_%S_")
+ self.__name100Hz = nome + "100Hz.csv"
+ self.__name10Hz = nome + "10Hz.csv"
+ self.__name4Hz = nome + "4Hz.csv"
+ self.__fieldnames100Hz = [
+ 'rpm', 'tps', 'accel_x', 'accel_y', 'accel_z', 'gyro_x', 'gyro_y',
+ 'gyro_z', 'pot_fsx', 'pot_fdx', 'pot_FAccuracy', 'pot_rsx',
+ 'pot_rdx', 'pot_RAccuracy', 'steeringEncoder'
+ ]
+ self.__fieldnames10Hz = [
+ 't_h20', 't_air', 't_oil', 'vbb', 'lambda1_avg', 'lambda1_raw',
+ 'k_lambda1', 'inj_low', 'inj_high'
+ ]
+ self.__fieldnames4Hz = [''] # DA FARE
+ with open(self.__name100Hz, 'w', newline='') as file100Hz:
+ writer = csv.writer(file100Hz, delimiter=';', dialect='excel')
+ writer.writerow(self.__fieldnames100Hz)
+ with open(self.__name10Hz, 'w', newline='') as file10Hz:
+ writer = csv.writer(file10Hz, delimiter=';', dialect='excel')
+ writer.writerow(self.__fieldnames10Hz)
+ with open(self.__name4Hz, 'w', newline='') as file4Hz:
+ writer = csv.writer(file4Hz, delimiter=';', dialect='excel')
+ writer.writerow(self.__fieldnames4Hz)
+
+
+# Appends data to the file created before
+
+ def write100Hz(self):
+ engineFrame100Hz = self.__dataFrame.getEngineFrame()
+ wheelFrame100Hz = self.__dataFrame.getWheelSensorsFrame()
+ gyroscopeFrame100Hz = self.__dataFrame.getGyroscopeFrame()
+ FrameValues100Hz = [engineFrame100Hz["rpm"], engineFrame100Hz["tps"]]
+ FrameValues100Hz.append(gyroscopeFrame100Hz['accel_x'])
+ FrameValues100Hz.append(gyroscopeFrame100Hz['accel_y'])
+ FrameValues100Hz.append(gyroscopeFrame100Hz['accel_z'])
+ FrameValues100Hz.append(gyroscopeFrame100Hz['gyro_x'])
+ FrameValues100Hz.append(gyroscopeFrame100Hz['gyro_y'])
+ FrameValues100Hz.append(gyroscopeFrame100Hz['gyro_z'])
+ FrameValues100Hz.append(wheelFrame100Hz['pot_fsx'])
+ FrameValues100Hz.append(wheelFrame100Hz['pot_fdx'])
+ FrameValues100Hz.append(wheelFrame100Hz['potFAccuracy'])
+ FrameValues100Hz.append(wheelFrame100Hz['pot_rsx'])
+ FrameValues100Hz.append(wheelFrame100Hz['pot_rdx'])
+ FrameValues100Hz.append(wheelFrame100Hz['potRAccuracy'])
+ FrameValues100Hz.append(wheelFrame100Hz['steeringEncoder'])
+ # gpsFrameValues = list(self.__dataFrame.getGPSFrame().values())
+ # wheelSensorsFrameValues = list(self.__dataFrame.getWheelSensorsFrame().values()) +
+ # gyroscopeFrameValues = list(self.__dataFrame.getGyroscopeFrame().values())
+ with open(self.__name100Hz, 'a', newline='') as csvfile100Hz:
+ writer = csv.writer(csvfile100Hz, delimiter=';', dialect='excel')
+ writer.writerow(FrameValues100Hz)
+
+ def write10Hz(self):
+ engineFrame10Hz = self.__dataFrame.getEngineFrame()
+ FrameValues10Hz = [
+ engineFrame10Hz["t_h20"], engineFrame10Hz["t_air"],
+ engineFrame10Hz["t_oil"], engineFrame10Hz["vbb"],
+ engineFrame10Hz["lambda1_avg"], engineFrame10Hz["lambda1_raw"],
+ engineFrame10Hz["k_lambda1"], engineFrame10Hz["inj_low"],
+ engineFrame10Hz["inj_high"]
+ ]
+ with open(self.__name10Hz, 'a', newline='') as csvfile10Hz:
+ writer = csv.writer(csvfile10Hz, delimiter=';', dialect='excel')
+ writer.writerow(FrameValues10Hz)
diff --git a/src/lib/serialhandler/SerialHandler.py b/src/lib/serialhandler/SerialHandler.py
new file mode 100644
index 0000000..4abf9e5
--- /dev/null
+++ b/src/lib/serialhandler/SerialHandler.py
@@ -0,0 +1,151 @@
+import serial
+from time import sleep
+
+
+class SerialHandler:
+
+ __doc__ = '''Documentation for SerialHandler.py functions:
+
+-__init__(name, baudrate, **kwargs):
+ -stopBit
+ -length = dataFrame length in bit (5, 6, 7, 8)
+ -parity = 'E', 'O', 'N'
+ -timeout = max Timeout in reading
+ -bytesToRead = Bytes to read from serial
+
+-openPort():
+ open the serial port and start communication
+
+-closePort():
+ close the communication and the serial port
+
+-readData(**kwargs):
+ read the data and return bytes with the read values, or b'ErrorReading' in case of failure
+ -size = Bytes to read only once
+ -startChar = byte that represent the start char for the sequence
+ WARNING the start char will not be included in return bytes sequence
+
+-getInfo():
+ return the dictionary with the settings for the serial port
+
+-clear():
+ clear the input buffer
+
+@classmethod
+scanCOMs():
+ return a list that contains the available ports (WORKS ONLY WITH WINDOWS O.S.)'''
+
+ def __init__(self, name, baudrate, **kwargs):
+ self.__portName = name
+ self.__baudRate = baudrate
+
+ if "stopBit" in kwargs.keys():
+ self.__stopBit = kwargs["stopBit"]
+ else:
+ self.__stopBit = 1
+
+ if "length" in kwargs.keys():
+ self.__wordLength = kwargs["length"]
+ else:
+ self.__wordLength = 8
+
+ if "parity" in kwargs.keys():
+ self.__wordParity = kwargs["parity"]
+ else:
+ self.__wordParity = 'N'
+
+ if "timeout" in kwargs.keys():
+ self.__timeout = kwargs["timeout"]
+ else:
+ self.__timeout = 600 / self.__baudRate
+
+ if "bytesToRead" in kwargs.keys():
+ self.__bytesToRead = kwargs["bytesToRead"]
+ else:
+ self.__bytesToRead = 1
+
+ self.__serialInstance = serial.Serial(port=self.__portName,
+ baudrate=self.__baudRate,
+ bytesize=self.__wordLength,
+ parity=self.__wordParity,
+ stopbits=self.__stopBit,
+ timeout=self.__timeout)
+ self.__serialInstance.close()
+
+ def readData(self, **kwargs):
+ messageRead = bytes()
+ if "size" in kwargs.keys():
+ bytesToRead = kwargs["size"]
+ else:
+ bytesToRead = self.__bytesToRead
+
+ if "startChar" in kwargs.keys() and not "endChar" in kwargs.keys():
+ attempt = 0
+ startChar = kwargs["startChar"]
+ while True:
+ charReceived = self.__serialInstance.read(size=1)
+ if charReceived == startChar:
+ for i in range(0, bytesToRead):
+ messageRead += (self.__serialInstance.read(1))
+ break
+ elif attempt >= bytesToRead:
+ return b'ReadError'
+ else:
+ attempt += 1
+
+ elif "startChar" in kwargs.keys() and "endChar" in kwargs.keys():
+ attempt = 0
+ startChar = kwargs["startChar"]
+ while True:
+ charReceived = self.__serialInstance.read(size=1)
+ if charReceived == startChar:
+ charReceived = self.__serialInstance.read(size=1)
+ while charReceived != kwargs["endChar"]:
+ messageRead += charReceived
+ charReceived = self.__serialInstance.read(size=1)
+ if charReceived == kwargs["endChar"]:
+ break
+ elif attempt >= 200:
+ if len(messageRead) > 0:
+ return messageRead[0] + b'ReadError'
+ else:
+ return b'ReadError'
+ else:
+ attempt += 1
+ else:
+ for i in range(0, bytesToRead):
+ messageRead += (self.__serialInstance.read(1))
+
+ return messageRead
+
+ def writeData(self, **kwargs):
+ if "byte" in kwargs.keys():
+ self.__serialInstance.write(kwargs["byte"])
+ else:
+ self.__serialInstance.write(b'\x01')
+ self.__serialInstance.flush()
+
+ def getInfo(self):
+ return self.__serialInstance.get_settings()
+
+ def openPort(self):
+ self.__serialInstance.open()
+ sleep(2)
+
+ def closePort(self):
+ self.__serialInstance.close()
+
+ # TO-DO: scanCOMs works also for Linux O.S.
+ # Method to list ALL the Serial ports (ONLY FOR WINDOWS)
+ @classmethod
+ def scanCOMs(cls):
+ portList = []
+ for i in range(255):
+ try:
+ portToCheck = 'COM' + str(i)
+ s = serial.Serial(portToCheck)
+ s.close()
+ portList.append(portToCheck)
+ except serial.SerialException:
+ pass
+ return portList
diff --git a/src/lib/serialhandler/__init__.py b/src/lib/serialhandler/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/serialhandler/test.py b/src/lib/serialhandler/test.py
new file mode 100644
index 0000000..bda8bc6
--- /dev/null
+++ b/src/lib/serialhandler/test.py
@@ -0,0 +1,23 @@
+from DataFrame import DataFrame
+from SerialHandler import SerialHandler
+from FormatData import FormatData
+from FileHandler import FileHandler
+from time import sleep
+
+df = DataFrame()
+fh = FileHandler(df)
+sh = SerialHandler("/dev/ttyACM0", 115200)
+sh.openPort()
+
+try:
+ while True:
+ data = sh.readData(startChar=b'\x02', endChar=b'\x03')
+
+ FormatData.setData(df, data, fh)
+ print(df)
+ sleep(.01)
+
+except KeyboardInterrupt:
+ sh.closePort()
+
+print("FINE")
diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts
new file mode 100644
index 0000000..49a2a16
--- /dev/null
+++ b/src/reportWebVitals.ts
@@ -0,0 +1,15 @@
+import { ReportHandler } from 'web-vitals';
+
+const reportWebVitals = (onPerfEntry?: ReportHandler) => {
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+ getCLS(onPerfEntry);
+ getFID(onPerfEntry);
+ getFCP(onPerfEntry);
+ getLCP(onPerfEntry);
+ getTTFB(onPerfEntry);
+ });
+ }
+};
+
+export default reportWebVitals;
diff --git a/src/setupTests.ts b/src/setupTests.ts
new file mode 100644
index 0000000..8f2609b
--- /dev/null
+++ b/src/setupTests.ts
@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';
diff --git a/src/styles/fonts.sass b/src/styles/fonts.sass
new file mode 100644
index 0000000..11fe4c8
--- /dev/null
+++ b/src/styles/fonts.sass
@@ -0,0 +1,13 @@
+@font-face
+ font-family: 'Open Sans'
+ font-style: normal
+ font-weight: 400
+ src: url('../assets/open-sans-v17-latin_cyrillic-regular.woff') format('woff'),
+ font-family: 'Open Sans'
+ font-style: italic
+ font-weight: 400
+ src: url('../assets/open-sans-v17-latin_cyrillic-italic.woff') format('woff'),
+ font-family: 'Roboto Condensed'
+ font-style: normal
+ font-weight: 400
+ src: url('../assets/roboto-condensed-v18-latin_cyrillic-regular.woff') format('woff'),
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..a273b0c
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": [
+ "src"
+ ]
+}