init commit
This commit is contained in:
		
						commit
						e37e4b4d54
					
				
					 10 changed files with 3072 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								.envrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.envrc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					use nix
 | 
				
			||||||
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					.direnv/
 | 
				
			||||||
 | 
					target/
 | 
				
			||||||
 | 
					*.obt
 | 
				
			||||||
							
								
								
									
										2570
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2570
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										17
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "obt"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					obd2={ path = "../obd2", features = [ "ftdi_comm" ] }
 | 
				
			||||||
 | 
					env_logger = "0.10"
 | 
				
			||||||
 | 
					vin_parser = "1.0.0"
 | 
				
			||||||
 | 
					chrono = "0.4.40"
 | 
				
			||||||
 | 
					anyhow = "1.0.97"
 | 
				
			||||||
 | 
					log = "0.4"
 | 
				
			||||||
 | 
					gpsd_client = "0.1.5"
 | 
				
			||||||
 | 
					sqlx = { version = "0.8.3", features = ["runtime-async-std", "sqlite"]}
 | 
				
			||||||
 | 
					bincode = "1.3.3"
 | 
				
			||||||
 | 
					serde = "1.0.219"
 | 
				
			||||||
 | 
					serde_json = "1.0.140"
 | 
				
			||||||
							
								
								
									
										4
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					# On-Board Telematics (OBT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					On-Board Telematics is a suite of software to capture data from your automobile
 | 
				
			||||||
 | 
					as well as (in the future) remotely control your automobile. 
 | 
				
			||||||
							
								
								
									
										8
									
								
								shell.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								shell.nix
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					{ pkgs ? import <nixpkgs> {} }:
 | 
				
			||||||
 | 
					pkgs.mkShell {
 | 
				
			||||||
 | 
					  packages = [ pkgs.rustc pkgs.cargo pkgs.libftdi1 pkgs.systemd pkgs.pkg-config pkgs.alsa-lib pkgs.ncurses pkgs.openssl ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  shellHook = ''
 | 
				
			||||||
 | 
					    export DEBUG=1
 | 
				
			||||||
 | 
					  '';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										110
									
								
								src/auto_events/auto_events.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/auto_events/auto_events.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,110 @@
 | 
				
			||||||
 | 
					use serde::{Serialize, Deserialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct SpeedEvent {
 | 
				
			||||||
 | 
					    pub speed: u32
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct GPSSpeedEvent {
 | 
				
			||||||
 | 
					    pub speed: u32
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct RPMEvent {
 | 
				
			||||||
 | 
					    pub rpm: u32
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct FuelLevelEvent {
 | 
				
			||||||
 | 
					    pub level_pct: f32
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct GPSLocationEvent {
 | 
				
			||||||
 | 
					    pub lat: f64,
 | 
				
			||||||
 | 
					    pub lng: f64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct ThrottleEvent {
 | 
				
			||||||
 | 
					    pub throttle_pct: f32 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub enum AutoEventType {
 | 
				
			||||||
 | 
					    Speed(SpeedEvent),
 | 
				
			||||||
 | 
					    RPM(RPMEvent),
 | 
				
			||||||
 | 
					    FuelLevel(FuelLevelEvent),
 | 
				
			||||||
 | 
					    GPSLocation(GPSLocationEvent),
 | 
				
			||||||
 | 
					    Throttle(ThrottleEvent),
 | 
				
			||||||
 | 
					    GPSSpeed(GPSSpeedEvent)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug,Serialize,Deserialize,PartialEq,Clone)]
 | 
				
			||||||
 | 
					pub struct AutoEvent {
 | 
				
			||||||
 | 
					    pub content: Vec<AutoEventType>,
 | 
				
			||||||
 | 
					    pub timestamp: i64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RPMEvent {
 | 
				
			||||||
 | 
					    pub fn new(rpm: u32) -> AutoEventType {
 | 
				
			||||||
 | 
					        return AutoEventType::RPM( 
 | 
				
			||||||
 | 
					                        RPMEvent {
 | 
				
			||||||
 | 
					                            rpm
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ThrottleEvent {
 | 
				
			||||||
 | 
					    pub fn new(throttle_pct: f32) -> AutoEventType {
 | 
				
			||||||
 | 
					        return AutoEventType::Throttle( 
 | 
				
			||||||
 | 
					                        ThrottleEvent {
 | 
				
			||||||
 | 
					                            throttle_pct
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SpeedEvent {
 | 
				
			||||||
 | 
					    pub fn new(speed: u32) -> AutoEventType {
 | 
				
			||||||
 | 
					        return AutoEventType::Speed( 
 | 
				
			||||||
 | 
					                        SpeedEvent {
 | 
				
			||||||
 | 
					                            speed
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GPSSpeedEvent {
 | 
				
			||||||
 | 
					    pub fn new(speed: u32) -> AutoEventType {
 | 
				
			||||||
 | 
					        return AutoEventType::GPSSpeed( 
 | 
				
			||||||
 | 
					                        GPSSpeedEvent {
 | 
				
			||||||
 | 
					                            speed
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FuelLevelEvent {
 | 
				
			||||||
 | 
					    pub fn new(level_pct: f32) -> AutoEventType {
 | 
				
			||||||
 | 
					        return AutoEventType::FuelLevel( 
 | 
				
			||||||
 | 
					                        FuelLevelEvent {
 | 
				
			||||||
 | 
					                            level_pct
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GPSLocationEvent {
 | 
				
			||||||
 | 
					    pub fn new(lat: f64, lng: f64) -> AutoEventType {
 | 
				
			||||||
 | 
					        return AutoEventType::GPSLocation( 
 | 
				
			||||||
 | 
					                        GPSLocationEvent {
 | 
				
			||||||
 | 
					                            lat,
 | 
				
			||||||
 | 
					                            lng
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								src/auto_events/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/auto_events/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					pub mod auto_events;
 | 
				
			||||||
 | 
					pub use auto_events::*;
 | 
				
			||||||
							
								
								
									
										111
									
								
								src/file_format.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/file_format.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					use crate::auto_events::AutoEvent;
 | 
				
			||||||
 | 
					use std::io::prelude::*;
 | 
				
			||||||
 | 
					use serde::{Serialize, Deserialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Serialize, Deserialize, PartialEq, Debug)]
 | 
				
			||||||
 | 
					pub enum AvailableAutoEventType {
 | 
				
			||||||
 | 
					    Speed = 1,
 | 
				
			||||||
 | 
					    RPM = 2,
 | 
				
			||||||
 | 
					    FuelLevel = 3,
 | 
				
			||||||
 | 
					    GPSLocation = 4,
 | 
				
			||||||
 | 
					    Throttle = 5
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Serialize, Deserialize, PartialEq, Debug)]
 | 
				
			||||||
 | 
					pub struct FileHeader {
 | 
				
			||||||
 | 
					    magic_number: u32,
 | 
				
			||||||
 | 
					    version: u8,
 | 
				
			||||||
 | 
					    event_types: Vec<AvailableAutoEventType>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Serialize, Deserialize, PartialEq, Debug)]
 | 
				
			||||||
 | 
					pub struct FileContents {
 | 
				
			||||||
 | 
					    header: FileHeader,
 | 
				
			||||||
 | 
					    records: Vec<AutoEvent>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct File {
 | 
				
			||||||
 | 
					    file_handle: std::fs::File,
 | 
				
			||||||
 | 
					    contents: FileContents
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl File {
 | 
				
			||||||
 | 
					    pub fn create(path: &str) -> ::anyhow::Result<Self> {
 | 
				
			||||||
 | 
					        let new_file = File {
 | 
				
			||||||
 | 
					            file_handle: std::fs::File::create_new(path)?,
 | 
				
			||||||
 | 
					            contents: FileContents {
 | 
				
			||||||
 | 
					                header: FileHeader {
 | 
				
			||||||
 | 
					                    magic_number: 0x0BD2_2018,
 | 
				
			||||||
 | 
					                    version: 1,
 | 
				
			||||||
 | 
					                    event_types: vec![
 | 
				
			||||||
 | 
					                        AvailableAutoEventType::Speed,
 | 
				
			||||||
 | 
					                        AvailableAutoEventType::RPM,
 | 
				
			||||||
 | 
					                        AvailableAutoEventType::FuelLevel,
 | 
				
			||||||
 | 
					                        AvailableAutoEventType::GPSLocation,
 | 
				
			||||||
 | 
					                        AvailableAutoEventType::Throttle,
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                records: vec![]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Ok(new_file);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn open(path: &str) -> ::anyhow::Result<Self> {
 | 
				
			||||||
 | 
					        println!("0");
 | 
				
			||||||
 | 
					        let file_bytes: Vec<u8> = std::fs::read(path)?;
 | 
				
			||||||
 | 
					        println!("1");
 | 
				
			||||||
 | 
					        let handle = std::fs::OpenOptions::new().read(true).write(true).open(path)?;
 | 
				
			||||||
 | 
					        println!("2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //let magic_number = bincode::deserialize::<u32>(&file_bytes[0..3])?;
 | 
				
			||||||
 | 
					        //let version = bincode::deserialize::<u8>(&file_bytes[4..4])?;
 | 
				
			||||||
 | 
					        println!("3");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //if magic_number != 0x0BD2_2018 {
 | 
				
			||||||
 | 
					        //    return Err(anyhow::anyhow!("Bad magic number. Is this an obt file?"));
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let contents = bincode::deserialize::<FileContents>(file_bytes.as_slice())?;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if contents.header.version != 1 {
 | 
				
			||||||
 | 
					            return Err(anyhow::anyhow!("File version {} is unsupported. We only support file version 1.", contents.header.version));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            file_handle: handle,
 | 
				
			||||||
 | 
					            contents
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_record(&mut self, record: AutoEvent) {
 | 
				
			||||||
 | 
					        self.contents.records.push(record);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn write(&mut self) -> ::anyhow::Result<()> {
 | 
				
			||||||
 | 
					        self.file_handle.set_len(0)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let new_contents = bincode::serialize::<FileContents>(&self.contents)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.file_handle.write_all(&new_contents)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.file_handle.flush()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn write_json(&mut self) -> ::anyhow::Result<()> {
 | 
				
			||||||
 | 
					        println!("$");
 | 
				
			||||||
 | 
					        self.file_handle.set_len(0)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let new_contents = serde_json::to_string::<FileContents>(&self.contents)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.file_handle.write_all(&new_contents.as_bytes())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.file_handle.flush()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										246
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,246 @@
 | 
				
			||||||
 | 
					use obd2::{commands::Obd2DataRetrieval, device::{Elm327, SerialPort}, Obd2};
 | 
				
			||||||
 | 
					use std::thread;
 | 
				
			||||||
 | 
					use std::thread::JoinHandle;
 | 
				
			||||||
 | 
					use std::time::Duration;
 | 
				
			||||||
 | 
					use anyhow::anyhow;
 | 
				
			||||||
 | 
					use std::sync::{Arc, Mutex};
 | 
				
			||||||
 | 
					use std::collections::VecDeque;
 | 
				
			||||||
 | 
					use gpsd_client::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod auto_events;
 | 
				
			||||||
 | 
					use auto_events::{AutoEvent, AutoEventType};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod file_format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static THREAD_DELAY: Duration = Duration::from_millis(1_000);
 | 
				
			||||||
 | 
					static trip_id: Mutex<u32> = Mutex::new(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_speed(device: &mut Obd2::<Elm327<SerialPort>>) -> ::anyhow::Result<AutoEventType> {
 | 
				
			||||||
 | 
					    if let Some(speed_kmph) = device.get_speed()?.first() {
 | 
				
			||||||
 | 
					        let speed_kmph_u32: u32 = speed_kmph.clone().into();
 | 
				
			||||||
 | 
					        let speed_mph: u32 = (speed_kmph_u32 * 621371) / 1000000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(auto_events::SpeedEvent::new(speed_mph))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(anyhow!("Unable to read speed from OBDII"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_rpm(device: &mut Obd2::<Elm327<SerialPort>>) -> ::anyhow::Result<AutoEventType> {
 | 
				
			||||||
 | 
					    if let Some(rpm) = device.get_rpm()?.first() {
 | 
				
			||||||
 | 
					        Ok(auto_events::RPMEvent::new(rpm.round() as u32))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(anyhow!("Unable to read rpm from OBDII"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_throttle(device: &mut Obd2::<Elm327<SerialPort>>) -> ::anyhow::Result<AutoEventType> {
 | 
				
			||||||
 | 
					    if let Some(throttle_level) = device.get_throttle_position()?.first() {
 | 
				
			||||||
 | 
					        Ok(auto_events::ThrottleEvent::new(f32::from(throttle_level.clone())/255.0))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(anyhow!("Unable to read rpm from OBDII"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_fuel_level(device: &mut Obd2::<Elm327<SerialPort>>) -> ::anyhow::Result<AutoEventType> {
 | 
				
			||||||
 | 
					    if let Some(fuel_level) = device.get_fuel_level()?.first() {
 | 
				
			||||||
 | 
					        Ok(auto_events::FuelLevelEvent::new(f32::from(fuel_level.clone())/255.0))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(anyhow!("Unable to read rpm from OBDII"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn wait_for_reconnect(device: &mut Option<Obd2<Elm327<SerialPort>>>, vin: &mut Option<String>) {
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        thread::sleep(THREAD_DELAY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *device = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Ok(port) = SerialPort::new("/dev/ttyUSB0") {
 | 
				
			||||||
 | 
					            match Elm327::new(port) {
 | 
				
			||||||
 | 
					                Ok(new_device) =>{
 | 
				
			||||||
 | 
					                if let Ok(mut dev) = Obd2::new(new_device) {
 | 
				
			||||||
 | 
					                    if let Err(x) = dev.reset() {
 | 
				
			||||||
 | 
					                        log::error!("OBDII initialization failed, retrying... {}", x);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    log::info!("Connected to OBDII");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    match dev.get_vin() {
 | 
				
			||||||
 | 
					                        Ok(new_vin) => {
 | 
				
			||||||
 | 
					                            *vin = Some(new_vin.clone());
 | 
				
			||||||
 | 
					                            match vin_parser::get_info(new_vin.as_str()) {
 | 
				
			||||||
 | 
					                                Ok(vin_result) => {
 | 
				
			||||||
 | 
					                                    let year = vin_result.years().last().map_or("Unknown Year".to_string(), |y| y.to_string());
 | 
				
			||||||
 | 
					                                    log::info!("Vehicle is: {} {}", vin_result.manufacturer, year);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                Err(_) => log::warn!("Unable to decode VIN"),
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Err(_) => {
 | 
				
			||||||
 | 
					                            *vin = None;
 | 
				
			||||||
 | 
					                            log::warn!("Unable to read VIN");
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    *device = Some(dev);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    log::warn!("Reconnect failed. Ignition may be off.");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                Err(x)=>{
 | 
				
			||||||
 | 
					                    log::warn!("Reconnect failed. Ignition may be off. {}", x);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn event_handler(events: Arc<Mutex<VecDeque<AutoEvent>>>) {
 | 
				
			||||||
 | 
					    thread::sleep(THREAD_DELAY / 2);
 | 
				
			||||||
 | 
					    let mut cur_tid: u32 = trip_id.lock().unwrap().clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let cur_datetime = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap();
 | 
				
			||||||
 | 
					        let mut file = file_format::File::create(format!("./out_{}.obt", cur_datetime.as_secs()).as_str()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            thread::sleep(THREAD_DELAY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let tid = trip_id.lock().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if *tid > cur_tid {
 | 
				
			||||||
 | 
					               file.write().unwrap(); 
 | 
				
			||||||
 | 
					               cur_tid = tid.clone();
 | 
				
			||||||
 | 
					               break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let ref mut el = events.lock().unwrap();
 | 
				
			||||||
 | 
					            while !el.is_empty() {
 | 
				
			||||||
 | 
					                if let Some(event) = el.pop_front() {
 | 
				
			||||||
 | 
					                    log::info!("{:?}", event);
 | 
				
			||||||
 | 
					                    file.add_record(event);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn unix_ts() -> i64 {
 | 
				
			||||||
 | 
					    let time  = chrono::Utc::now();
 | 
				
			||||||
 | 
					    time.timestamp_millis()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn event_getter(events: Arc<Mutex<VecDeque<AutoEvent>>>, device: Arc<Mutex<Option<Obd2<Elm327<SerialPort>>>>>, vin: Arc<Mutex<Option<String>>>) {
 | 
				
			||||||
 | 
					    let event_getters: Vec<fn(&mut Obd2::<Elm327<SerialPort>>) -> ::anyhow::Result<AutoEventType>> = vec![
 | 
				
			||||||
 | 
					        get_speed, 
 | 
				
			||||||
 | 
					        get_rpm,
 | 
				
			||||||
 | 
					        get_throttle,
 | 
				
			||||||
 | 
					        get_fuel_level
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut ignition_on: bool = false;
 | 
				
			||||||
 | 
					    let mut gps: GPS = GPS::connect().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'record_loop: loop {
 | 
				
			||||||
 | 
					        thread::sleep(THREAD_DELAY);
 | 
				
			||||||
 | 
					        let mut full_event: AutoEvent = AutoEvent{ content: vec![], timestamp: 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for event in &event_getters {
 | 
				
			||||||
 | 
					            let ref mut x = device.lock().unwrap();
 | 
				
			||||||
 | 
					            if let Some(ref mut dev) = *(*x) {
 | 
				
			||||||
 | 
					                match event(dev) {
 | 
				
			||||||
 | 
					                    Ok(event) => {
 | 
				
			||||||
 | 
					                        // Derive ignition on/off events from RPM 
 | 
				
			||||||
 | 
					                        if let AutoEventType::RPM(ev) = event.clone() {
 | 
				
			||||||
 | 
					                            if ev.rpm > 0 && !ignition_on{
 | 
				
			||||||
 | 
					                                log::info!("Ignition ON");
 | 
				
			||||||
 | 
					                                ignition_on = true;
 | 
				
			||||||
 | 
					                                continue 'record_loop;
 | 
				
			||||||
 | 
					                            } else if ignition_on && ev.rpm == 0 {
 | 
				
			||||||
 | 
					                                ignition_on = false;
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    let mut mtx = trip_id.lock().unwrap();
 | 
				
			||||||
 | 
					                                    *mtx += 1;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                log::info!("Ignition OFF");
 | 
				
			||||||
 | 
					                                continue 'record_loop;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if ignition_on {
 | 
				
			||||||
 | 
					                            full_event.content.push(event);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    Err(err) => {
 | 
				
			||||||
 | 
					                        if let Some(obd2::Error::Device(obd2::error::DeviceError(obd2::device::Error::IO(io_err)))) = err.downcast_ref::<obd2::Error>() {
 | 
				
			||||||
 | 
					                            if io_err.kind() == std::io::ErrorKind::BrokenPipe {
 | 
				
			||||||
 | 
					                                log::warn!("Disconnected from OBDII reader. Ignition may be off. Attempting to reconnect.");
 | 
				
			||||||
 | 
					                                wait_for_reconnect(x, &mut vin.lock().unwrap());
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else if let Some(obd2::Error::Device(obd2::error::DeviceError(obd2::device::Error::Communication(_)))) = err.downcast_ref::<obd2::Error>() {
 | 
				
			||||||
 | 
					                                log::error!("OBDII connection was broken. Resetting connection.");
 | 
				
			||||||
 | 
					                                wait_for_reconnect(x, &mut vin.lock().unwrap());
 | 
				
			||||||
 | 
					                            log::error!("{}", err);
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            log::error!("{}", err);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } 
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if full_event.content.len() == 0 {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let data: GPSData = gps.current_data().unwrap();
 | 
				
			||||||
 | 
					        full_event.content.push(auto_events::GPSLocationEvent::new(data.lat, data.lon));
 | 
				
			||||||
 | 
					        let speed_ms = data.speed;
 | 
				
			||||||
 | 
					        let speed_mph = speed_ms * 2.2369362921; 
 | 
				
			||||||
 | 
					        full_event.content.push(auto_events::GPSSpeedEvent::new(speed_mph.round() as u32));
 | 
				
			||||||
 | 
					        full_event.timestamp = unix_ts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events.lock().unwrap().push_back(full_event);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() -> ::anyhow::Result<()> {
 | 
				
			||||||
 | 
					    file_format::File::open("weis_drive.obt")?.write_json()?;
 | 
				
			||||||
 | 
					    return Ok(());
 | 
				
			||||||
 | 
					    env_logger::init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let events: Arc<Mutex<VecDeque<AutoEvent>>> = Arc::new(Mutex::new(vec![].into()));
 | 
				
			||||||
 | 
					    let device: Arc<Mutex<Option<Obd2<Elm327<SerialPort>>>>> = Arc::new(Mutex::new(None));
 | 
				
			||||||
 | 
					    let vin: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    log::info!("Starting On Board Telematics v1.0.0");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    wait_for_reconnect(&mut *device.lock().unwrap(), &mut vin.lock().unwrap());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Start worker threads
 | 
				
			||||||
 | 
					    let mut threads: Vec<JoinHandle<()>> = vec![];
 | 
				
			||||||
 | 
					    let mut events_cloned = Arc::clone(&events);
 | 
				
			||||||
 | 
					    /*hreads.push(*//*)*/
 | 
				
			||||||
 | 
					    thread::spawn(move|| {event_handler(events_cloned)});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    events_cloned = Arc::clone(&events);
 | 
				
			||||||
 | 
					    let device_cloned = Arc::clone(&device);
 | 
				
			||||||
 | 
					    let vin_cloned = Arc::clone(&vin);
 | 
				
			||||||
 | 
					    threads.push(thread::spawn(move|| {event_getter(events_cloned, device_cloned, vin_cloned)}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for thread in threads {
 | 
				
			||||||
 | 
					        thread.join().unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue