chore: micodus_server rework #1
1770
Cargo.lock
generated
1770
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@ bitvec = { version = "1.0" }
|
|||||||
chrono = { version = "0.4" }
|
chrono = { version = "0.4" }
|
||||||
encoding_rs = { version = "0.8" }
|
encoding_rs = { version = "0.8" }
|
||||||
lazy_static = { version = "1.5" }
|
lazy_static = { version = "1.5" }
|
||||||
|
libsql = { version = "0.9", features = ["replication"] }
|
||||||
rand = { version = "0.9" }
|
rand = { version = "0.9" }
|
||||||
rusqlite = { version = "0.34", features = ["bundled"] }
|
rusqlite = { version = "0.37", features = ["bundled"] }
|
||||||
tokio = { version = "1.44", features = ["full", "sync"] }
|
tokio = { version = "1.46", features = ["full", "sync"] }
|
||||||
|
10
html/const.lua
Normal file
10
html/const.lua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
basepath = "/home/paul/git/micodus_server"
|
||||||
|
dbfile = string.format("%s/data/tracker.db",basepath)
|
||||||
|
query = [[
|
||||||
|
SELECT time,latitude,longitude,height,speed,direction,serial
|
||||||
|
FROM log
|
||||||
|
ORDER BY id DESC, serial DESC
|
||||||
|
LIMIT 1;
|
||||||
|
]]
|
||||||
|
|
||||||
|
return {["basepath"]=basepath, ["dbfile"]=dbfile, ["query"]=query}
|
@ -1,14 +1,12 @@
|
|||||||
/*
|
//engine.js
|
||||||
*/
|
|
||||||
|
|
||||||
let map;
|
let map;
|
||||||
let point;
|
let point;
|
||||||
|
|
||||||
const arrows = ["↑", "↗", "→", "↘", "↓", "↙", "←", "↖"];
|
const arrows = ["↑", "↗", "→", "↘", "↓", "↙", "←", "↖"];
|
||||||
|
const socket = new WebSocket("wss://trackme.ovh/ws");
|
||||||
|
|
||||||
const socket = new WebSocket("wss://geo.paulbsd.com/ws");
|
function create_location(coords, text) {
|
||||||
|
|
||||||
function create_location(coords,text) {
|
|
||||||
point = L.marker(coords, {
|
point = L.marker(coords, {
|
||||||
color: 'red',
|
color: 'red',
|
||||||
fillColor: '#f03',
|
fillColor: '#f03',
|
||||||
@ -26,8 +24,9 @@ function create_map(coords) {
|
|||||||
});
|
});
|
||||||
L.tileLayer(`https://tile.openstreetmap.org/{z}/{x}/{y}.png`, {
|
L.tileLayer(`https://tile.openstreetmap.org/{z}/{x}/{y}.png`, {
|
||||||
maxZoom: 19,
|
maxZoom: 19,
|
||||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> / <a href="https://www.paulbsd.com">PaulBSD</a>'
|
attribution: `© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> / <a href="https://www.paulbsd.com">PaulBSD</a>`
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
map.zoomControl.setPosition('bottomright');
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(data) {
|
function update(data) {
|
||||||
@ -35,27 +34,62 @@ function update(data) {
|
|||||||
const speed = data.speed/10;
|
const speed = data.speed/10;
|
||||||
let section = parseInt(data.direction/45 + 0.5);
|
let section = parseInt(data.direction/45 + 0.5);
|
||||||
section = section % 8;
|
section = section % 8;
|
||||||
console.log(arrows[section]);
|
const text = `time: ${data.time}<br/>latitude: ${data.latitude}<br/>longitude: ${data.longitude}<br/>height: ${data.height}<br/>speed: ${speed}<br/>direction: ${arrows[section]} ${data.direction}°<br/>serial: ${data.serial}`;
|
||||||
const text = `time: ${data.time}<br/>latitude: ${data.latitude}<br/>longitude: ${data.longitude}<br/>height: ${data.height}<br/>speed: ${speed}<br/>direction: ${arrows[section]} ${data.direction}`;
|
if (map) {
|
||||||
if (!map) {
|
|
||||||
create_map(coords);
|
|
||||||
} else {
|
|
||||||
map.setView(coords);
|
map.setView(coords);
|
||||||
map.flyTo(coords);
|
map.flyTo(coords);
|
||||||
}
|
|
||||||
if (!point) {
|
|
||||||
create_location(coords, text);
|
|
||||||
} else {
|
} else {
|
||||||
|
create_map(coords);
|
||||||
|
}
|
||||||
|
if (point) {
|
||||||
point.setLatLng(coords);
|
point.setLatLng(coords);
|
||||||
point.setPopupContent(text);
|
point.setPopupContent(text);
|
||||||
|
} else {
|
||||||
|
create_location(coords, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ping() {
|
||||||
|
socket.send("ping");
|
||||||
|
}
|
||||||
|
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
update(data);
|
update(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener("open", (event) => {
|
socket.addEventListener("open", (event) => {
|
||||||
|
console.log(event);
|
||||||
socket.send("ping");
|
socket.send("ping");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setInterval(ping, 1000)
|
||||||
|
|
||||||
|
// service worker
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('/engine.js')
|
||||||
|
.then((reg) => {
|
||||||
|
console.log('Enregistrement réussi');
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log('Erreur : ' + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const CACHE_NAME = 'my-site-cache-v1';
|
||||||
|
const urlsToCache = [
|
||||||
|
'/',
|
||||||
|
'/leaflet/leaflet.js',
|
||||||
|
'/engine.js',
|
||||||
|
'/style.css',
|
||||||
|
];
|
||||||
|
|
||||||
|
self.addEventListener('install', function(event) {
|
||||||
|
// Perform install steps
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(function(cache) {
|
||||||
|
console.log('Opened cache');
|
||||||
|
return cache.addAll(urlsToCache);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
BIN
html/favicon.ico
Normal file
BIN
html/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
@ -1,6 +1,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<title>PaulBSD geo</title>
|
<title>Trackme</title>
|
||||||
<link rel="icon" href="https://paulbsd.com/favicon.ico" type="image/x-icon">
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||||
<link rel="stylesheet" href="leaflet/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
<link rel="stylesheet" href="leaflet/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||||
<link rel="stylesheet" href="style.css" crossorigin=""/>
|
<link rel="stylesheet" href="style.css" crossorigin=""/>
|
||||||
<script src="leaflet/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin="">
|
<script src="leaflet/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin="">
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
#!/usr/bin/lua
|
#!/usr/bin/lua
|
||||||
--ngx.say(_VERSION)
|
|
||||||
|
|
||||||
|
local const = require('const')
|
||||||
|
local json = require("json")
|
||||||
local sqlite = require("lsqlite3")
|
local sqlite = require("lsqlite3")
|
||||||
local basepath = "/home/paul/git/micodus_server"
|
|
||||||
local dbfile = string.format("%s/data/tracker.db",basepath)
|
|
||||||
--local output = string.format("%s/html/lastloc.json",basepath)
|
--local output = string.format("%s/html/lastloc.json",basepath)
|
||||||
local query = [[
|
|
||||||
SELECT latitude,longitude
|
|
||||||
FROM log
|
|
||||||
ORDER BY time DESC
|
|
||||||
LIMIT 1;
|
|
||||||
]]
|
|
||||||
|
|
||||||
function main()
|
function main()
|
||||||
local db = sqlite.open(dbfile,sqlite3.OPEN_READONLY)
|
local db = sqlite.open(const.dbfile,sqlite.OPEN_READONLY)
|
||||||
|
|
||||||
local res, vm = db:nrows(query)
|
local res, vm = db:nrows(const.query)
|
||||||
for row in res, vm do
|
for row in res, vm do
|
||||||
local locstr = string.format("{\"latitude\": %s, \"longitude\": %s}", row.latitude, row.longitude)
|
local locstr = string.format("{\"latitude\": %s, \"longitude\": %s}", row.latitude, row.longitude)
|
||||||
--f = io.open(output, "w")
|
--f = io.open(output, "w")
|
||||||
|
43
html/old/engine.js
Normal file
43
html/old/engine.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
let map;
|
||||||
|
let circle;
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
get_data().then(c=> {
|
||||||
|
const d = [c.latitude,c.longitude];
|
||||||
|
if (!map) {
|
||||||
|
map = L.map('map', {
|
||||||
|
center: d,
|
||||||
|
zoom: 13
|
||||||
|
});
|
||||||
|
L.tileLayer(`https://tile.openstreetmap.org/{z}/{x}/{y}.png`, {
|
||||||
|
maxZoom: 19,
|
||||||
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||||
|
}).addTo(map);
|
||||||
|
}
|
||||||
|
if (circle) {
|
||||||
|
circle.remove();
|
||||||
|
circle=null;
|
||||||
|
}
|
||||||
|
if (circle == null) {
|
||||||
|
circle = L.circle(d, {
|
||||||
|
color: 'red',
|
||||||
|
fillColor: '#f03',
|
||||||
|
fillOpacity: 0.5,
|
||||||
|
radius: 50
|
||||||
|
}).addTo(map).bindPopup('I\'m here.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_data() {
|
||||||
|
const res = fetch("lastloc.json").then((a)=> {
|
||||||
|
const b = a.json().then((j) => {
|
||||||
|
return j;
|
||||||
|
})
|
||||||
|
return b;
|
||||||
|
})
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
setInterval(update,10000);
|
1
html/old/lastloc.json
Normal file
1
html/old/lastloc.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"latitude": 49.173069, "longitude": -0.342916}
|
6
html/old/test.html
Normal file
6
html/old/test.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<script>
|
||||||
|
window.navigator.vibrate(1000);
|
||||||
|
document.write("<h1>"+window.navigator.userAgent+"</h1>");
|
||||||
|
</script>
|
||||||
|
</html>
|
54
html/old/ws_test.lua
Normal file
54
html/old/ws_test.lua
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
local server = require "nginx.websocket.server"
|
||||||
|
|
||||||
|
function geows()
|
||||||
|
local wb, err = server:new {
|
||||||
|
timeout = 5000,
|
||||||
|
max_payload_len = 65535
|
||||||
|
}
|
||||||
|
if not wb then
|
||||||
|
ngx.log(ngx.ERR, "failed to new websocket: ", err)
|
||||||
|
return ngx.exit(444)
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local bytes, err = wb:send_text(string.format("%s haha!",data))
|
||||||
|
if not bytes then
|
||||||
|
ngx.log(ngx.ERR, "failed to send text: ", err)
|
||||||
|
return ngx.exit(444)
|
||||||
|
end
|
||||||
|
ngx.sleep(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[while true do
|
||||||
|
local data, typ, err = wb:recv_frame()
|
||||||
|
if wb.fatal then
|
||||||
|
ngx.log(ngx.ERR, "failed to receive frame: ", err)
|
||||||
|
return ngx.exit(444)
|
||||||
|
end
|
||||||
|
if not data then
|
||||||
|
local bytes, err = wb:send_ping()
|
||||||
|
if not bytes then
|
||||||
|
ngx.log(ngx.ERR, "failed to send ping: ", err)
|
||||||
|
return ngx.exit(444)
|
||||||
|
end
|
||||||
|
elseif typ == "close" then break
|
||||||
|
elseif typ == "ping" then
|
||||||
|
local bytes, err = wb:send_pong()
|
||||||
|
if not bytes then
|
||||||
|
ngx.log(ngx.ERR, "failed to send pong: ", err)
|
||||||
|
return ngx.exit(444)
|
||||||
|
end
|
||||||
|
elseif typ == "pong" then
|
||||||
|
ngx.log(ngx.INFO, "client ponged")
|
||||||
|
elseif typ == "text" then
|
||||||
|
local bytes, err = wb:send_text(string.format("%s haha!",data))
|
||||||
|
if not bytes then
|
||||||
|
ngx.log(ngx.ERR, "failed to send text: ", err)
|
||||||
|
return ngx.exit(444)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end--]]
|
||||||
|
wb:send_close()
|
||||||
|
end
|
||||||
|
|
||||||
|
geows()
|
@ -1,8 +1,8 @@
|
|||||||
// Create WebSocket connection.
|
// Create WebSocket connection.
|
||||||
const socket = new WebSocket("wss://geo.paulbsd.com/ws");
|
const socket = new WebSocket("wss://trackme.ovh/ws");
|
||||||
|
|
||||||
// Connection opened
|
// Connection opened
|
||||||
socket.addEventListener("open", (event) => {
|
socket.addEventListener("open", (_event) => {
|
||||||
socket.send("Hello Server!");
|
socket.send("Hello Server!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
37
html/ws.lua
37
html/ws.lua
@ -1,36 +1,30 @@
|
|||||||
#!/usr/bin/lua
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
package.path = package.path..";/home/paul/git/micodus_server/html/?.lua"
|
||||||
|
|
||||||
|
local const = require("const")
|
||||||
local json = require("json")
|
local json = require("json")
|
||||||
local server = require("nginx.websocket.server")
|
local server = require("nginx.websocket.server")
|
||||||
local sqlite = require("lsqlite3")
|
local sqlite = require("lsqlite3")
|
||||||
|
|
||||||
local basepath = "/home/paul/git/micodus_server"
|
|
||||||
local dbfile = string.format("%s/data/tracker.db",basepath)
|
|
||||||
local query = [[
|
|
||||||
SELECT time,latitude,longitude,height,speed,direction,serial
|
|
||||||
FROM log
|
|
||||||
ORDER BY time DESC
|
|
||||||
LIMIT 1;
|
|
||||||
]]
|
|
||||||
|
|
||||||
--ngx.shared.geo:set("last_time","")
|
--ngx.shared.geo:set("last_time","")
|
||||||
|
local db = sqlite.open(const.dbfile, sqlite.OPEN_READONLY)
|
||||||
|
|
||||||
function getdata()
|
function getdata()
|
||||||
local db = sqlite.open(dbfile,sqlite3.OPEN_READONLY)
|
local res, vm = db:nrows(const.query)
|
||||||
local res, vm = db:nrows(query)
|
local data = {}
|
||||||
local data
|
|
||||||
for row in res, vm do
|
for row in res, vm do
|
||||||
data = {
|
data = {
|
||||||
["time"]=row.time,
|
["time"] = row.time,
|
||||||
["latitude"]=row.latitude,
|
["latitude"] = row.latitude,
|
||||||
["longitude"]=row.longitude,
|
["longitude"] = row.longitude,
|
||||||
["height"]=row.height,
|
["height"] = row.height,
|
||||||
["speed"]=row.speed,
|
["speed"] = row.speed,
|
||||||
["direction"]=row.direction,
|
["direction"] = row.direction,
|
||||||
["serial"]=row.serial,
|
["serial"] = row.serial,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
db:close()
|
-- db:close()
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -49,6 +43,7 @@ function geows()
|
|||||||
while true do
|
while true do
|
||||||
local data = getdata()
|
local data = getdata()
|
||||||
if data.time ~= last_time then
|
if data.time ~= last_time then
|
||||||
|
--do
|
||||||
local locstr = json.encode(data)
|
local locstr = json.encode(data)
|
||||||
local bytes, err = wb:send_text(locstr)
|
local bytes, err = wb:send_text(locstr)
|
||||||
if not bytes then
|
if not bytes then
|
||||||
@ -57,7 +52,7 @@ function geows()
|
|||||||
end
|
end
|
||||||
last_time = data.time
|
last_time = data.time
|
||||||
end
|
end
|
||||||
ngx.sleep(1)
|
ngx.sleep(0.5)
|
||||||
end
|
end
|
||||||
wb:send_close()
|
wb:send_close()
|
||||||
end
|
end
|
||||||
|
103
src/db.rs
103
src/db.rs
@ -1,103 +0,0 @@
|
|||||||
use rusqlite::{types::*, *};
|
|
||||||
|
|
||||||
const DBPATH: &'static str = "data/tracker.db";
|
|
||||||
const STATEMENTS: &'static [&str] = &[
|
|
||||||
"CREATE TABLE log (
|
|
||||||
id integer primary key autoincrement,
|
|
||||||
time text,
|
|
||||||
serial integer,
|
|
||||||
latitude float,
|
|
||||||
longitude float,
|
|
||||||
speed integer,
|
|
||||||
height integer,
|
|
||||||
direction integer,
|
|
||||||
is_satellite bool);",
|
|
||||||
"CREATE INDEX idx_time ON log (time);",
|
|
||||||
"CREATE INDEX idx_serial ON log (serial);",
|
|
||||||
];
|
|
||||||
const QUERY_INSERT: &'static str = "
|
|
||||||
INSERT INTO log (
|
|
||||||
time,
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
speed,
|
|
||||||
height,
|
|
||||||
direction,
|
|
||||||
serial,
|
|
||||||
is_satellite
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
:time,
|
|
||||||
:latitude,
|
|
||||||
:longitude,
|
|
||||||
:speed,
|
|
||||||
:height,
|
|
||||||
:direction,
|
|
||||||
:serial,
|
|
||||||
:is_satellite
|
|
||||||
)";
|
|
||||||
|
|
||||||
pub fn connectdb() -> Result<Connection> {
|
|
||||||
let conn = Connection::open(DBPATH)?;
|
|
||||||
Ok(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initdb(conn: &Connection) -> Result<()> {
|
|
||||||
create_tables(&conn)?;
|
|
||||||
set_pragmas(&conn)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_tables(conn: &Connection) -> Result<()> {
|
|
||||||
for s in STATEMENTS {
|
|
||||||
match conn.execute(s, ()) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => println!("update failed: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_pragmas(conn: &Connection) -> Result<()> {
|
|
||||||
conn.pragma_update(Some(DatabaseName::Main), "journal_mode", "WAL")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_insert(conn: &Connection) -> Statement {
|
|
||||||
conn.prepare(QUERY_INSERT).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(conn: &Connection, dblog: &DbLog) -> Result<()> {
|
|
||||||
let mut stmt = prepare_insert(&conn);
|
|
||||||
match stmt.execute(params![
|
|
||||||
dblog.time,
|
|
||||||
dblog.latitude,
|
|
||||||
dblog.longitude,
|
|
||||||
dblog.speed,
|
|
||||||
dblog.height,
|
|
||||||
dblog.direction,
|
|
||||||
dblog.serial,
|
|
||||||
dblog.is_satellite,
|
|
||||||
]) {
|
|
||||||
Ok(i) => println!("{} rows were inserted", i),
|
|
||||||
Err(err) => println!("insert failed: {}", err),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DbLog {
|
|
||||||
pub time: String,
|
|
||||||
pub latitude: f64,
|
|
||||||
pub longitude: f64,
|
|
||||||
pub speed: u16,
|
|
||||||
pub height: u16,
|
|
||||||
pub direction: u16,
|
|
||||||
pub serial: u16,
|
|
||||||
pub is_satellite: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToSql for DbLog {
|
|
||||||
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
|
||||||
Ok(self.time.to_sql().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
114
src/db/libsql_engine.rs
Normal file
114
src/db/libsql_engine.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use libsql::*;
|
||||||
|
|
||||||
|
impl LibSQLEngine {
|
||||||
|
pub const TABLE_STATEMENTS: &'static [&str] = &[
|
||||||
|
"CREATE TABLE log (
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
time text,
|
||||||
|
serial integer,
|
||||||
|
latitude float,
|
||||||
|
longitude float,
|
||||||
|
speed integer,
|
||||||
|
height integer,
|
||||||
|
direction integer,
|
||||||
|
is_satellite bool
|
||||||
|
);",
|
||||||
|
"CREATE INDEX idx_time ON log (time);",
|
||||||
|
"CREATE INDEX idx_serial ON log (serial);",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub const QUERY_INSERT: &'static str = "
|
||||||
|
INSERT INTO log (
|
||||||
|
time,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
speed,
|
||||||
|
height,
|
||||||
|
direction,
|
||||||
|
serial,
|
||||||
|
is_satellite
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
:time,
|
||||||
|
:latitude,
|
||||||
|
:longitude,
|
||||||
|
:speed,
|
||||||
|
:height,
|
||||||
|
:direction,
|
||||||
|
:serial,
|
||||||
|
:is_satellite
|
||||||
|
);";
|
||||||
|
|
||||||
|
pub const DBPATH: &'static str = "data/tracker.db";
|
||||||
|
|
||||||
|
async fn initdb(&self) {
|
||||||
|
self.create_tables().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_tables(&self) -> Result<()> {
|
||||||
|
for stmt in Self::TABLE_STATEMENTS {
|
||||||
|
match self.conn.as_ref().unwrap().execute(stmt, ()).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => println!("update failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prepare_insert(&self) -> Statement {
|
||||||
|
self.conn
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.prepare(Self::QUERY_INSERT)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Engine for LibSQLEngine {
|
||||||
|
#[tokio::main]
|
||||||
|
async fn connect(&mut self) {
|
||||||
|
println!("{}", self.path.clone().unwrap_or("".to_string()));
|
||||||
|
let path = match self.path.clone() {
|
||||||
|
Some(o) => o,
|
||||||
|
None => Self::DBPATH.to_string(),
|
||||||
|
};
|
||||||
|
let b = Builder::new_remote_replica(path, "".into(), "".into())
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
self.conn = Some(b.connect().unwrap());
|
||||||
|
self.initdb().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn insert(&mut self, dblog: &DbLog) {
|
||||||
|
let mut stmt = self.prepare_insert().await;
|
||||||
|
match stmt
|
||||||
|
.execute(params![
|
||||||
|
dblog.time.clone(),
|
||||||
|
dblog.latitude,
|
||||||
|
dblog.longitude,
|
||||||
|
dblog.speed,
|
||||||
|
dblog.height,
|
||||||
|
dblog.direction,
|
||||||
|
dblog.serial,
|
||||||
|
dblog.is_satellite,
|
||||||
|
])
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(i) => println!("{} rows were inserted", i),
|
||||||
|
Err(err) => println!("insert failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct LibSQLEngine {
|
||||||
|
pub conn: Option<Connection>,
|
||||||
|
pub path: Option<String>,
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub token: Option<String>,
|
||||||
|
}
|
77
src/db/mod.rs
Normal file
77
src/db/mod.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//pub mod libsql_engine;
|
||||||
|
pub mod sqlite_engine;
|
||||||
|
|
||||||
|
pub enum SQLEngine {
|
||||||
|
SQLite(sqlite_engine::SQLiteEngine),
|
||||||
|
//LibSQL(libsql_engine::LibSQLEngine),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Engine {
|
||||||
|
fn connect(&mut self);
|
||||||
|
fn init(&mut self);
|
||||||
|
fn insert(&mut self, dblog: &DbLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for SQLEngine {
|
||||||
|
fn connect(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::SQLite(engine) => engine.connect(),
|
||||||
|
//Self::LibSQL(engine) => engine.connect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::SQLite(engine) => engine.init(),
|
||||||
|
//Self::LibSQL(engine) => engine.connect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, dblog: &DbLog) {
|
||||||
|
match self {
|
||||||
|
Self::SQLite(engine) => engine.insert(&dblog),
|
||||||
|
//Self::LibSQL(engine) => engine.connect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sqlite_engine::SQLiteEngine> for SQLEngine {
|
||||||
|
fn from(store: sqlite_engine::SQLiteEngine) -> Self {
|
||||||
|
Self::SQLite(store)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*impl From<libsql_engine::LibSQLEngine> for SQLEngine {
|
||||||
|
fn from(store: libsql_engine::LibSQLEngine) -> Self {
|
||||||
|
Self::LibSQL(store)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct DbLog {
|
||||||
|
pub time: String,
|
||||||
|
pub latitude: f64,
|
||||||
|
pub longitude: f64,
|
||||||
|
pub speed: u16,
|
||||||
|
pub height: u16,
|
||||||
|
pub direction: u16,
|
||||||
|
pub serial: u16,
|
||||||
|
pub is_satellite: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for DbLog {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} {} {} {} {} {} {} ",
|
||||||
|
self.time,
|
||||||
|
self.latitude,
|
||||||
|
self.longitude,
|
||||||
|
self.speed,
|
||||||
|
self.height,
|
||||||
|
self.direction,
|
||||||
|
self.serial,
|
||||||
|
self.is_satellite,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
115
src/db/sqlite_engine.rs
Normal file
115
src/db/sqlite_engine.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use rusqlite::{types::*, *};
|
||||||
|
|
||||||
|
impl SQLiteEngine {
|
||||||
|
pub const DBPATH: &'static str = "data/tracker.db";
|
||||||
|
|
||||||
|
pub const TABLE_STATEMENTS: &'static [&str] = &[
|
||||||
|
"CREATE TABLE IF NOT EXISTS log (
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
time text,
|
||||||
|
serial integer,
|
||||||
|
latitude float,
|
||||||
|
longitude float,
|
||||||
|
speed integer,
|
||||||
|
height integer,
|
||||||
|
direction integer,
|
||||||
|
is_satellite bool
|
||||||
|
);",
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_time ON log (time);",
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_serial ON log (serial);",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub const QUERY_INSERT: &'static str = "
|
||||||
|
INSERT INTO log (
|
||||||
|
time,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
speed,
|
||||||
|
height,
|
||||||
|
direction,
|
||||||
|
serial,
|
||||||
|
is_satellite
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
:time,
|
||||||
|
:latitude,
|
||||||
|
:longitude,
|
||||||
|
:speed,
|
||||||
|
:height,
|
||||||
|
:direction,
|
||||||
|
:serial,
|
||||||
|
:is_satellite
|
||||||
|
);";
|
||||||
|
|
||||||
|
fn create_tables(&mut self) -> Result<()> {
|
||||||
|
for s in Self::TABLE_STATEMENTS {
|
||||||
|
match self.conn.as_ref().unwrap().execute(s, ()) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => println!("update failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pragmas(&self) -> Result<()> {
|
||||||
|
self.conn
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.pragma_update(None, "journal_mode", "WAL")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_insert(&self) -> Statement {
|
||||||
|
self.conn
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.prepare(Self::QUERY_INSERT)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for DbLog {
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
|
Ok(self.time.to_sql().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Engine for SQLiteEngine {
|
||||||
|
fn connect(&mut self) {
|
||||||
|
let path = match self.path.clone() {
|
||||||
|
Some(o) => o,
|
||||||
|
None => Self::DBPATH.to_string(),
|
||||||
|
};
|
||||||
|
self.conn = Some(Connection::open(path).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.create_tables().unwrap();
|
||||||
|
self.set_pragmas().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, dblog: &DbLog) {
|
||||||
|
let mut stmt = self.prepare_insert();
|
||||||
|
match stmt.execute(params![
|
||||||
|
dblog.time,
|
||||||
|
dblog.latitude,
|
||||||
|
dblog.longitude,
|
||||||
|
dblog.speed,
|
||||||
|
dblog.height,
|
||||||
|
dblog.direction,
|
||||||
|
dblog.serial,
|
||||||
|
dblog.is_satellite,
|
||||||
|
]) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => println!("insert failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SQLiteEngine {
|
||||||
|
pub conn: Option<Connection>,
|
||||||
|
pub path: Option<String>,
|
||||||
|
}
|
0
src/lib.rs
Normal file
0
src/lib.rs
Normal file
111
src/main.rs
111
src/main.rs
@ -1,23 +1,20 @@
|
|||||||
mod db;
|
pub mod db;
|
||||||
mod parser;
|
pub mod parser;
|
||||||
|
pub mod serve;
|
||||||
|
|
||||||
use crate::db::*;
|
//pub use db::libsql_engine::LibSQLEngine;
|
||||||
use crate::parser::*;
|
pub use db::sqlite_engine::SQLiteEngine;
|
||||||
|
pub use db::*;
|
||||||
use std::io;
|
pub use parser::*;
|
||||||
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
|
|
||||||
const ADDR: &'static str = "0.0.0.0";
|
|
||||||
const PORT: u64 = 7701;
|
|
||||||
const BUFSIZE: usize = 1024;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
//test();
|
let mut s: SQLEngine = SQLEngine::SQLite(SQLiteEngine::default());
|
||||||
let conn = connectdb().unwrap();
|
s.connect();
|
||||||
initdb(&conn).unwrap();
|
s.init();
|
||||||
apiserver().await.unwrap();
|
|
||||||
|
let receiver = serve::control_server().await;
|
||||||
|
serve::micodus_protocol_server(receiver).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -28,85 +25,3 @@ fn test() {
|
|||||||
println!("{code}");
|
println!("{code}");
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn apiserver() -> io::Result<()> {
|
|
||||||
let listener = match TcpListener::bind(format!("{}:{}", ADDR, PORT)).await {
|
|
||||||
Ok(o) => o,
|
|
||||||
Err(e) => {
|
|
||||||
println!("error: {e}");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let (socket, _remote_addr) = listener.accept().await?;
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let mut buf = vec![0; BUFSIZE];
|
|
||||||
'nest: loop {
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
let terminal_id = 0;
|
|
||||||
match socket.readable().await {
|
|
||||||
Ok(_) => {
|
|
||||||
match socket.try_read(&mut buf) {
|
|
||||||
Ok(_) => match handle(&buf) {
|
|
||||||
Some(o) => match socket.try_write(&o) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
println!("error: {e}");
|
|
||||||
break 'nest;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
break 'nest;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
println!("error read: {e}");
|
|
||||||
if e.kind() == io::ErrorKind::WouldBlock {
|
|
||||||
continue 'nest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("error socket readable: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match socket.writable().await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
println!("error socket writable: {e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle(buf: &Vec<u8>) -> Option<Vec<u8>> {
|
|
||||||
let mut rawdata = InboundDataWrapper::new(buf.to_vec());
|
|
||||||
let reply = match parse_inbound_msg(&mut rawdata) {
|
|
||||||
Ok(o) => {
|
|
||||||
println!("query: {}", o);
|
|
||||||
//println!("raw query: {:X?}", o.to_raw());
|
|
||||||
Message::store(&o);
|
|
||||||
Message::set_reply(o)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("parse inbound message error: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match reply {
|
|
||||||
Some(o) => {
|
|
||||||
println!("reply: {}", o);
|
|
||||||
//println!("raw reply {:X?}", o.to_raw());
|
|
||||||
println!("--------------");
|
|
||||||
return Some(o.to_raw().into());
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -376,7 +376,6 @@ impl LocationInformationReport {
|
|||||||
|
|
||||||
fn parse_time(&mut self, timeslice: [u8; 6]) {
|
fn parse_time(&mut self, timeslice: [u8; 6]) {
|
||||||
let code = BcdNumber::try_from(×lice as &[u8]).unwrap();
|
let code = BcdNumber::try_from(×lice as &[u8]).unwrap();
|
||||||
println!("{}", code);
|
|
||||||
let time = format!("{}", code.to_u64().unwrap());
|
let time = format!("{}", code.to_u64().unwrap());
|
||||||
match NaiveDateTime::parse_from_str(time.as_str(), "%y%m%d%H%M%S") {
|
match NaiveDateTime::parse_from_str(time.as_str(), "%y%m%d%H%M%S") {
|
||||||
Ok(o) => {
|
Ok(o) => {
|
||||||
@ -521,6 +520,26 @@ impl From<u32> for LocationInformationReportStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct LocationInformationQuery {}
|
||||||
|
impl LocationInformationQuery {
|
||||||
|
pub const ID: u16 = 0x8201;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BodyMessage for LocationInformationQuery {
|
||||||
|
fn parse(&mut self, rawbody: &Vec<u8>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct LocationInformationQueryResponse {}
|
||||||
|
impl LocationInformationQueryResponse {
|
||||||
|
pub const ID: u16 = 0x0201;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BodyMessage for LocationInformationQueryResponse {
|
||||||
|
fn parse(&mut self, rawbody: &Vec<u8>) {}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct StartOfTrip {}
|
pub struct StartOfTrip {}
|
||||||
impl StartOfTrip {
|
impl StartOfTrip {
|
||||||
@ -583,6 +602,8 @@ generate_impl!(
|
|||||||
QueryTerminalParameterResponse,
|
QueryTerminalParameterResponse,
|
||||||
TerminalControl,
|
TerminalControl,
|
||||||
LocationInformationReport,
|
LocationInformationReport,
|
||||||
|
LocationInformationQuery,
|
||||||
|
LocationInformationQueryResponse,
|
||||||
StartOfTrip,
|
StartOfTrip,
|
||||||
EndOfTrip
|
EndOfTrip
|
||||||
);
|
);
|
||||||
|
@ -89,7 +89,7 @@ impl std::fmt::Display for MessageHeader {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"id: {:X?}, length: {}, terminal id: {}, serial: {:X?}",
|
"id: {:#06x}, length: {}, terminal id: {}, serial: {:X?}",
|
||||||
self.id, self.bodylength, self.terminal_id, self.serial_number
|
self.id, self.bodylength, self.terminal_id, self.serial_number
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
mod body;
|
pub mod body;
|
||||||
mod error;
|
pub mod error;
|
||||||
mod header;
|
pub mod header;
|
||||||
|
|
||||||
use body::*;
|
pub use body::*;
|
||||||
use error::*;
|
pub use error::*;
|
||||||
use header::*;
|
pub use header::*;
|
||||||
|
|
||||||
use crate::db::*;
|
pub use super::db::*;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
@ -253,100 +253,91 @@ impl Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_reply(inmsg: Message) -> Option<Message> {
|
pub fn send_reply(inmsg: Option<Message>) -> Option<Message> {
|
||||||
let mut reply: Message = Message::default();
|
let mut reply: Message = Message::default();
|
||||||
let terminal_id = inmsg.header.get_raw_terminal_id().clone();
|
match inmsg {
|
||||||
match inmsg.content {
|
Some(inmsg) => {
|
||||||
MessageType::TerminalRegistration(t) => {
|
let terminal_id = inmsg.header.get_raw_terminal_id().clone();
|
||||||
let cnt = t.generate_reply(terminal_id.into(), inmsg.header.serial_number);
|
match inmsg.content {
|
||||||
reply.header.build(
|
MessageType::TerminalRegistration(t) => {
|
||||||
TerminalRegistrationReply::ID,
|
let content =
|
||||||
cnt.to_raw().len(),
|
t.generate_reply(terminal_id.into(), inmsg.header.serial_number);
|
||||||
terminal_id,
|
reply.header.build(
|
||||||
);
|
TerminalRegistrationReply::ID,
|
||||||
reply.content = MessageType::TerminalRegistrationReply(cnt);
|
content.to_raw().len(),
|
||||||
}
|
terminal_id,
|
||||||
MessageType::TerminalAuthentication(t) => {
|
);
|
||||||
let cnt = t.generate_reply(TerminalAuthentication::ID, inmsg.header.serial_number);
|
reply.content = MessageType::TerminalRegistrationReply(content);
|
||||||
reply.header.build(
|
}
|
||||||
PlatformUniversalResponse::ID,
|
MessageType::TerminalAuthentication(t) => {
|
||||||
cnt.to_raw().len(),
|
let content = t
|
||||||
terminal_id,
|
.generate_reply(TerminalAuthentication::ID, inmsg.header.serial_number);
|
||||||
);
|
reply.header.build(
|
||||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
PlatformUniversalResponse::ID,
|
||||||
}
|
content.to_raw().len(),
|
||||||
MessageType::LocationInformationReport(t) => {
|
terminal_id,
|
||||||
let cnt =
|
);
|
||||||
t.generate_reply(LocationInformationReport::ID, inmsg.header.serial_number);
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||||
reply.header.build(
|
}
|
||||||
PlatformUniversalResponse::ID,
|
MessageType::LocationInformationReport(t) => {
|
||||||
cnt.to_raw().len(),
|
let content = t.generate_reply(
|
||||||
terminal_id,
|
LocationInformationReport::ID,
|
||||||
);
|
inmsg.header.serial_number,
|
||||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
);
|
||||||
}
|
reply.header.build(
|
||||||
MessageType::TerminalHeartbeat(t) => {
|
PlatformUniversalResponse::ID,
|
||||||
let cnt = t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
content.to_raw().len(),
|
||||||
reply.header.build(
|
terminal_id,
|
||||||
PlatformUniversalResponse::ID,
|
);
|
||||||
cnt.to_raw().len(),
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||||
terminal_id,
|
}
|
||||||
);
|
MessageType::TerminalHeartbeat(t) => {
|
||||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
let content =
|
||||||
}
|
t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
||||||
MessageType::TerminalLogout(t) => {
|
reply.header.build(
|
||||||
let cnt = t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
PlatformUniversalResponse::ID,
|
||||||
reply.header.build(
|
content.to_raw().len(),
|
||||||
PlatformUniversalResponse::ID,
|
terminal_id,
|
||||||
cnt.to_raw().len(),
|
);
|
||||||
terminal_id,
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||||
);
|
}
|
||||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
MessageType::TerminalLogout(t) => {
|
||||||
}
|
let content =
|
||||||
_ => {
|
t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
||||||
println!("no type");
|
reply.header.build(
|
||||||
return None;
|
PlatformUniversalResponse::ID,
|
||||||
|
content.to_raw().len(),
|
||||||
|
terminal_id,
|
||||||
|
);
|
||||||
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("no type");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
reply.outbound_finalize();
|
reply.outbound_finalize();
|
||||||
Some(reply)
|
Some(reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store(inmsg: &Message) {
|
pub fn store(inmsg: &Message) -> Option<DbLog> {
|
||||||
match inmsg.content {
|
let res = match inmsg.content {
|
||||||
MessageType::LocationInformationReport(ref t) => {
|
MessageType::LocationInformationReport(ref t) => Some(DbLog {
|
||||||
/*{
|
serial: inmsg.header.serial_number,
|
||||||
use std::fs::OpenOptions;
|
time: t.time.format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||||
use std::io::prelude::*;
|
latitude: t.latitude,
|
||||||
|
longitude: t.longitude,
|
||||||
let mut file = OpenOptions::new()
|
speed: t.speed,
|
||||||
.write(true)
|
height: t.height,
|
||||||
.append(true)
|
direction: t.direction,
|
||||||
.open("data/log.txt")
|
is_satellite: t.is_satellite(),
|
||||||
.unwrap();
|
}),
|
||||||
|
_ => None,
|
||||||
//if let Err(e) = writeln!(file, ) {
|
};
|
||||||
// eprintln!("Couldn't write to file: {}", e);
|
res
|
||||||
//}
|
|
||||||
file.write(format!("{},{},{}\n", t.time, t.latitude, t.longitude).as_bytes())
|
|
||||||
.unwrap();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
let conn = connectdb().unwrap();
|
|
||||||
let dblog = crate::db::DbLog {
|
|
||||||
serial: inmsg.header.serial_number,
|
|
||||||
time: t.time.format("%Y-%m-%d %H:%M:%S").to_string(),
|
|
||||||
latitude: t.latitude,
|
|
||||||
longitude: t.longitude,
|
|
||||||
speed: t.speed,
|
|
||||||
height: t.height,
|
|
||||||
direction: t.direction,
|
|
||||||
is_satellite: t.is_satellite(),
|
|
||||||
};
|
|
||||||
insert(&conn, &dblog).unwrap();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outbound_finalize(&mut self) {
|
pub fn outbound_finalize(&mut self) {
|
||||||
@ -457,3 +448,22 @@ impl std::fmt::Display for MessageType {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.append(true)
|
||||||
|
.open("data/log.txt")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
//if let Err(e) = writeln!(file, ) {
|
||||||
|
// eprintln!("Couldn't write to file: {}", e);
|
||||||
|
//}
|
||||||
|
file.write(format!("{},{},{}\n", t.time, t.latitude, t.longitude).as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
165
src/serve.rs
Normal file
165
src/serve.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
pub use crate::db::sqlite_engine::SQLiteEngine;
|
||||||
|
pub use crate::db::*;
|
||||||
|
pub use crate::parser::*;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
const ADDR: &'static str = "0.0.0.0";
|
||||||
|
const PORT: u64 = 7701;
|
||||||
|
const BUFSIZE: usize = 1024;
|
||||||
|
|
||||||
|
pub async fn control_server() -> Receiver<u8> {
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:7702").await.unwrap();
|
||||||
|
let (sender, receiver): (Sender<u8>, Receiver<u8>) = channel(100);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
let (socket, _) = listener.accept().await.unwrap();
|
||||||
|
socket.readable().await.unwrap();
|
||||||
|
let mut buf = [0; 16];
|
||||||
|
match socket.try_read(&mut buf) {
|
||||||
|
Ok(0) => {}
|
||||||
|
Ok(n) => {
|
||||||
|
let mut data = str::from_utf8(&buf).unwrap();
|
||||||
|
data = data.trim();
|
||||||
|
if data.starts_with("test") {
|
||||||
|
sender.send(1).await.unwrap();
|
||||||
|
println!("ok");
|
||||||
|
} else {
|
||||||
|
println!("n: {n}, msg: '{data}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("{e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn micodus_protocol_server(receiver: Receiver<u8>) {
|
||||||
|
let mut sql: SQLEngine = SQLEngine::SQLite(SQLiteEngine::default());
|
||||||
|
sql.connect();
|
||||||
|
|
||||||
|
let arc_recv = Arc::new(RwLock::new(receiver));
|
||||||
|
|
||||||
|
let listener = match TcpListener::bind(format!("{}:{}", ADDR, PORT)).await {
|
||||||
|
Ok(o) => o,
|
||||||
|
Err(e) => {
|
||||||
|
println!("error: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//let sql_ref = Arc::clone(&arc_s);
|
||||||
|
loop {
|
||||||
|
let recv_clone = Arc::clone(&arc_recv);
|
||||||
|
let (socket, _remote_addr) = listener.accept().await.unwrap();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
serve(&socket, &recv_clone).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn serve(socket: &TcpStream, a: &Arc<RwLock<Receiver<u8>>>) {
|
||||||
|
let mut buf = vec![0; BUFSIZE];
|
||||||
|
let aa = Arc::clone(&a);
|
||||||
|
loop {
|
||||||
|
let mut bb = aa.write().await;
|
||||||
|
//let terminal_id = 0;
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
readable = socket.readable() => {
|
||||||
|
match readable {
|
||||||
|
Ok(_) => {
|
||||||
|
match socket.try_read(&mut buf) {
|
||||||
|
Ok(_) => match handle(&buf) {
|
||||||
|
Some(o) => match socket.try_write(&o) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("error: {e}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("none");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() == io::ErrorKind::WouldBlock {
|
||||||
|
println!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("error socket readable: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send = bb.recv() => {
|
||||||
|
match socket.writable().await {
|
||||||
|
Ok(_) => {
|
||||||
|
match send {
|
||||||
|
Some(o) => match o {
|
||||||
|
1 => {
|
||||||
|
socket.try_write(&[o, 0x01]).unwrap();
|
||||||
|
println!("sent");
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("error socket not writable: {e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(buf: &Vec<u8>) -> Option<Vec<u8>> {
|
||||||
|
let mut rawdata = InboundDataWrapper::new(buf.to_vec());
|
||||||
|
//let sql = Arc::clone(s);
|
||||||
|
let reply = match parse_inbound_msg(&mut rawdata) {
|
||||||
|
Ok(o) => {
|
||||||
|
println!("query: {}", o);
|
||||||
|
//println!("raw query: {:X?}", o.to_raw());
|
||||||
|
match Message::store(&o) {
|
||||||
|
Some(log) => {
|
||||||
|
let mut s: SQLEngine = SQLEngine::SQLite(SQLiteEngine::default());
|
||||||
|
s.connect();
|
||||||
|
s.insert(&log);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
};
|
||||||
|
Message::send_reply(Some(o))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("parse inbound message error: {}", e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match reply {
|
||||||
|
Some(o) => {
|
||||||
|
println!("reply: {}", o);
|
||||||
|
//println!("raw reply {:X?}", o.to_raw());
|
||||||
|
println!("--------------");
|
||||||
|
return Some(o.to_raw().into());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
tools/sql.lua
Executable file
26
tools/sql.lua
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local sqlite = require("lsqlite3")
|
||||||
|
local basepath = "/home/paul/git/micodus_server"
|
||||||
|
local dbfile = string.format("%s/data/tracker.db",basepath)
|
||||||
|
local output = string.format("%s/html/lastloc.json",basepath)
|
||||||
|
local query = [[
|
||||||
|
SELECT latitude,longitude
|
||||||
|
FROM log
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
]]
|
||||||
|
|
||||||
|
function main()
|
||||||
|
local db = sqlite.open(dbfile)
|
||||||
|
|
||||||
|
local res, vm = db:nrows(query)
|
||||||
|
for row in res, vm do
|
||||||
|
local locstr = string.format("{\"latitude\": %s, \"longitude\": %s}", row.latitude, row.longitude)
|
||||||
|
f = io.open(output, "w")
|
||||||
|
f:write(locstr)
|
||||||
|
end
|
||||||
|
db:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
main()
|
3
tools/systemd_run.sh
Normal file
3
tools/systemd_run.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
sudo systemd-run --uid=paul --working-directory=/home/paul/git/micodus_server ./target/release/micodus_server
|
Loading…
Reference in New Issue
Block a user