Skip to content

Instantly share code, notes, and snippets.

@tayyebi
Created July 16, 2024 06:55
Show Gist options
  • Save tayyebi/bee558c081db8affb3973a954d43eb39 to your computer and use it in GitHub Desktop.
Save tayyebi/bee558c081db8affb3973a954d43eb39 to your computer and use it in GitHub Desktop.
Coban TK103 GPS Tracker Socket with RUST programming Language
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