Created
July 16, 2024 06:55
-
-
Save tayyebi/bee558c081db8affb3973a954d43eb39 to your computer and use it in GitHub Desktop.
Coban TK103 GPS Tracker Socket with RUST programming Language
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{Read, Write}; | |
use std::net::{TcpListener, TcpStream, Shutdown}; | |
extern crate reqwest; | |
extern crate serde_json; | |
use reqwest::blocking::Client; | |
use serde_json::json; | |
fn main() { | |
let api_url = "http://cb/api/heartbeats"; | |
let ip_address = "0.0.0.0"; | |
let port = "8000"; | |
let listener = TcpListener::bind(format!("{}:{}", ip_address, port)).expect("Failed to bind"); | |
println!("Listening on {}:{}", ip_address, port); | |
let mut client_sockets: Vec<TcpStream> = Vec::new(); | |
loop { | |
match listener.accept() { | |
Ok((mut stream, _)) => { | |
println!("New connection: {}", stream.peer_addr().unwrap()); | |
client_sockets.push(stream.try_clone().expect("Failed to clone socket")); | |
let mut buffer = [0; 128]; | |
match stream.read(&mut buffer) { | |
Ok(n) if n > 0 => { | |
let data = String::from_utf8_lossy(&buffer[..n]); | |
println!("Received data: {}", data); | |
let trimmed_data = data.trim_matches('#'); | |
let tk103_data: Vec<&str> = trimmed_data.split(',').collect(); | |
let mut response = String::new(); | |
match tk103_data.len() { | |
1 => { | |
response = String::from("ON"); | |
println!("Sent ON to client"); | |
} | |
3 if tk103_data[0] == "##" => { | |
response = String::from("LOAD"); | |
println!("Sent LOAD to client"); | |
} | |
13 | 19 => { | |
// *XX,YYYYYYYYYYYYYYY,V1,HHMMSS,S,latitude,D,longitude,G,speed,direction,DDMMYY,equ _status # | |
// *HQ,865205034645050,V1,191437,A,3657.11617,N,04531.60020,E,0.00,9,130724,FFFFFBFF# | |
// * command head | |
// , separator | |
// | |
// 0. XX Supplier name,ASCII character | |
// 1. YYYYYYYYYYYYYYY 15digit IMEI number | |
// 2. V1/V2/V3/V4 comamnd code | |
// 3. HHMMSS Time | |
// 4. data valid byte (A/V/B), | |
// A represent valid data signal, | |
// V represent invalid data signal, | |
// B represent Beidou valid data signal | |
// 5. Latitude latitude,format DDMM.MMMMM | |
// 6. D latitude symbol(N:north,S:south) | |
// 7. Longitude Longitude,format DDDMM.MMMMM | |
// 8. G Longitude symbol(E:East,W:west) | |
// 9. Speed range 000.00 ~ 999.99 byte For New Firmware need adjust protocol, Speed fixed at 3 digits : 1) if speed is 2 digits, it will generate 1 “space” before speed 2) if speed is 1 digits, it will generate 2 “space” before speed. | |
// 10. Direction direction,true north is 0 degree,resolution 1 dgree,clockwise | |
// 11. DDMMYY date/month/year | |
// 12. equ_status (refer < General data definition >) | |
// # ending | |
// Extract relevant fields | |
let supplier_name = &tk103_data[0][1..]; // Remove the asterisk (*) | |
let imei = tk103_data[1]; | |
let command_code = tk103_data[2]; | |
let time_value = tk103_data[3]; | |
let data_valid_byte = tk103_data[4]; | |
let latitude = | |
tk103_data[5][..tk103_data[5].len() - 1].parse::<f64>().ok(); | |
let latitude_symbol = tk103_data[6]; | |
let longitude = | |
tk103_data[7][..tk103_data[7].len() - 1].parse::<f64>().ok(); | |
let longitude_symbol = tk103_data[8]; | |
let speed = tk103_data[9].parse::<f64>().ok(); | |
let direction = tk103_data[10].parse::<i32>().ok(); | |
let date = tk103_data[11]; | |
let equ_status = tk103_data[12]; | |
// Construct the JSON body | |
let body = json!({ | |
"imei": imei, | |
"lat": degree_to_decimal(latitude, latitude_symbol), | |
"long": degree_to_decimal(longitude, longitude_symbol), | |
"speed": speed, // * 1.852 | |
// "recorded_at": gps_time | |
}); | |
println!("Request body: {}", body); | |
// Call the send_request function | |
send_request(body, api_url); | |
} | |
_ => {} | |
} | |
if !response.is_empty() { | |
stream | |
.write_all(response.as_bytes()) | |
.expect("Failed to send response"); | |
} | |
// Close the stream after sending the response | |
stream.shutdown(Shutdown::Both).ok(); | |
} | |
Ok(_) => { | |
// Empty data received | |
client_sockets | |
.retain(|s| s.peer_addr().unwrap() != stream.peer_addr().unwrap()); | |
println!( | |
"Client disconnected. Total clients: {}", | |
client_sockets.len() | |
); | |
} | |
Err(e) => { | |
eprintln!("Error reading from socket: {}", e); | |
// Log the error and continue execution | |
} | |
} | |
} | |
Err(e) => { | |
eprintln!("Error accepting connection: {}", e); | |
// Log the error and continue waiting for new connections | |
} | |
} | |
} | |
} | |
fn nmea_to_mysql_time(input_data: &str) -> Result<String, &'static str> { | |
// Validate input_data (you can add more checks if needed) | |
if input_data.len() != 9 { | |
return Err("Invalid input length"); | |
} | |
// Extract hours, minutes, and seconds | |
let hours = &input_data[0..2]; | |
let minutes = &input_data[2..4]; | |
let seconds = &input_data[4..6]; | |
let milliseconds = &input_data[7..9]; | |
// Construct MySQL-compatible time format | |
let mysql_time = format!("{}:{}:{}.{}", hours, minutes, seconds, milliseconds); | |
Ok(mysql_time) | |
} | |
fn degree_to_decimal(coordinates_in_degrees: Option<f64>, direction: &str) -> Option<f64> { | |
match coordinates_in_degrees { | |
Some(degrees) => { | |
// Calculate integer degrees | |
let integer_degrees = (degrees / 100.0) as i32; | |
// Calculate minutes (based on the remaining degrees) | |
let minutes = (degrees - (integer_degrees as f64 * 100.0)) / 60.0; | |
// Combine degrees and minutes | |
let mut coordinates_in_decimal = integer_degrees as f64 + minutes; | |
// Adjust for southern or western hemisphere | |
if direction == "S" || direction == "W" { | |
coordinates_in_decimal *= -1.0; | |
} | |
// Return the result | |
Some(coordinates_in_decimal) | |
} | |
None => None, | |
} | |
} | |
fn send_request(body: serde_json::Value, api_url: &str) -> Result<(), Box<dyn std::error::Error>> { | |
let client = reqwest::blocking::Client::new(); | |
let response = client.post(api_url).json(&body).send()?; | |
if response.status().is_success() { | |
println!("Request successful!"); | |
} else { | |
println!("Request failed: {:?}", response.status()); | |
} | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment