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" }
|
||||
encoding_rs = { version = "0.8" }
|
||||
lazy_static = { version = "1.5" }
|
||||
libsql = { version = "0.9", features = ["replication"] }
|
||||
rand = { version = "0.9" }
|
||||
rusqlite = { version = "0.34", features = ["bundled"] }
|
||||
tokio = { version = "1.44", features = ["full", "sync"] }
|
||||
rusqlite = { version = "0.37", features = ["bundled"] }
|
||||
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 point;
|
||||
|
||||
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, {
|
||||
color: 'red',
|
||||
fillColor: '#f03',
|
||||
@ -26,8 +24,9 @@ function create_map(coords) {
|
||||
});
|
||||
L.tileLayer(`https://tile.openstreetmap.org/{z}/{x}/{y}.png`, {
|
||||
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);
|
||||
map.zoomControl.setPosition('bottomright');
|
||||
}
|
||||
|
||||
function update(data) {
|
||||
@ -35,27 +34,62 @@ function update(data) {
|
||||
const speed = data.speed/10;
|
||||
let section = parseInt(data.direction/45 + 0.5);
|
||||
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}`;
|
||||
if (!map) {
|
||||
create_map(coords);
|
||||
} else {
|
||||
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}`;
|
||||
if (map) {
|
||||
map.setView(coords);
|
||||
map.flyTo(coords);
|
||||
}
|
||||
if (!point) {
|
||||
create_location(coords, text);
|
||||
} else {
|
||||
create_map(coords);
|
||||
}
|
||||
if (point) {
|
||||
point.setLatLng(coords);
|
||||
point.setPopupContent(text);
|
||||
} else {
|
||||
create_location(coords, text);
|
||||
}
|
||||
}
|
||||
|
||||
function ping() {
|
||||
socket.send("ping");
|
||||
}
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
update(data);
|
||||
});
|
||||
|
||||
socket.addEventListener("open", (event) => {
|
||||
console.log(event);
|
||||
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>
|
||||
<title>PaulBSD geo</title>
|
||||
<link rel="icon" href="https://paulbsd.com/favicon.ico" type="image/x-icon">
|
||||
<title>Trackme</title>
|
||||
<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="style.css" crossorigin=""/>
|
||||
<script src="leaflet/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin="">
|
||||
|
@ -1,21 +1,14 @@
|
||||
#!/usr/bin/lua
|
||||
--ngx.say(_VERSION)
|
||||
|
||||
local const = require('const')
|
||||
local json = require("json")
|
||||
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,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
|
||||
local locstr = string.format("{\"latitude\": %s, \"longitude\": %s}", row.latitude, row.longitude)
|
||||
--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.
|
||||
const socket = new WebSocket("wss://geo.paulbsd.com/ws");
|
||||
const socket = new WebSocket("wss://trackme.ovh/ws");
|
||||
|
||||
// Connection opened
|
||||
socket.addEventListener("open", (event) => {
|
||||
socket.addEventListener("open", (_event) => {
|
||||
socket.send("Hello Server!");
|
||||
});
|
||||
|
||||
|
37
html/ws.lua
37
html/ws.lua
@ -1,36 +1,30 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
package.path = package.path..";/home/paul/git/micodus_server/html/?.lua"
|
||||
|
||||
local const = require("const")
|
||||
local json = require("json")
|
||||
local server = require("nginx.websocket.server")
|
||||
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","")
|
||||
local db = sqlite.open(const.dbfile, sqlite.OPEN_READONLY)
|
||||
|
||||
function getdata()
|
||||
local db = sqlite.open(dbfile,sqlite3.OPEN_READONLY)
|
||||
local res, vm = db:nrows(query)
|
||||
local data
|
||||
local res, vm = db:nrows(const.query)
|
||||
local data = {}
|
||||
for row in res, vm do
|
||||
data = {
|
||||
["time"]=row.time,
|
||||
["latitude"]=row.latitude,
|
||||
["longitude"]=row.longitude,
|
||||
["height"]=row.height,
|
||||
["speed"]=row.speed,
|
||||
["direction"]=row.direction,
|
||||
["serial"]=row.serial,
|
||||
["time"] = row.time,
|
||||
["latitude"] = row.latitude,
|
||||
["longitude"] = row.longitude,
|
||||
["height"] = row.height,
|
||||
["speed"] = row.speed,
|
||||
["direction"] = row.direction,
|
||||
["serial"] = row.serial,
|
||||
}
|
||||
end
|
||||
db:close()
|
||||
-- db:close()
|
||||
return data
|
||||
end
|
||||
|
||||
@ -49,6 +43,7 @@ function geows()
|
||||
while true do
|
||||
local data = getdata()
|
||||
if data.time ~= last_time then
|
||||
--do
|
||||
local locstr = json.encode(data)
|
||||
local bytes, err = wb:send_text(locstr)
|
||||
if not bytes then
|
||||
@ -57,7 +52,7 @@ function geows()
|
||||
end
|
||||
last_time = data.time
|
||||
end
|
||||
ngx.sleep(1)
|
||||
ngx.sleep(0.5)
|
||||
end
|
||||
wb:send_close()
|
||||
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;
|
||||
mod parser;
|
||||
pub mod db;
|
||||
pub mod parser;
|
||||
pub mod serve;
|
||||
|
||||
use crate::db::*;
|
||||
use crate::parser::*;
|
||||
|
||||
use std::io;
|
||||
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
const ADDR: &'static str = "0.0.0.0";
|
||||
const PORT: u64 = 7701;
|
||||
const BUFSIZE: usize = 1024;
|
||||
//pub use db::libsql_engine::LibSQLEngine;
|
||||
pub use db::sqlite_engine::SQLiteEngine;
|
||||
pub use db::*;
|
||||
pub use parser::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
//test();
|
||||
let conn = connectdb().unwrap();
|
||||
initdb(&conn).unwrap();
|
||||
apiserver().await.unwrap();
|
||||
let mut s: SQLEngine = SQLEngine::SQLite(SQLiteEngine::default());
|
||||
s.connect();
|
||||
s.init();
|
||||
|
||||
let receiver = serve::control_server().await;
|
||||
serve::micodus_protocol_server(receiver).await;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -28,85 +25,3 @@ fn test() {
|
||||
println!("{code}");
|
||||
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]) {
|
||||
let code = BcdNumber::try_from(×lice as &[u8]).unwrap();
|
||||
println!("{}", code);
|
||||
let time = format!("{}", code.to_u64().unwrap());
|
||||
match NaiveDateTime::parse_from_str(time.as_str(), "%y%m%d%H%M%S") {
|
||||
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)]
|
||||
pub struct StartOfTrip {}
|
||||
impl StartOfTrip {
|
||||
@ -583,6 +602,8 @@ generate_impl!(
|
||||
QueryTerminalParameterResponse,
|
||||
TerminalControl,
|
||||
LocationInformationReport,
|
||||
LocationInformationQuery,
|
||||
LocationInformationQueryResponse,
|
||||
StartOfTrip,
|
||||
EndOfTrip
|
||||
);
|
||||
|
@ -89,7 +89,7 @@ impl std::fmt::Display for MessageHeader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
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
|
||||
)
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
#![allow(unused_variables)]
|
||||
mod body;
|
||||
mod error;
|
||||
mod header;
|
||||
pub mod body;
|
||||
pub mod error;
|
||||
pub mod header;
|
||||
|
||||
use body::*;
|
||||
use error::*;
|
||||
use header::*;
|
||||
pub use body::*;
|
||||
pub use error::*;
|
||||
pub use header::*;
|
||||
|
||||
use crate::db::*;
|
||||
pub use super::db::*;
|
||||
|
||||
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 terminal_id = inmsg.header.get_raw_terminal_id().clone();
|
||||
match inmsg.content {
|
||||
MessageType::TerminalRegistration(t) => {
|
||||
let cnt = t.generate_reply(terminal_id.into(), inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
TerminalRegistrationReply::ID,
|
||||
cnt.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::TerminalRegistrationReply(cnt);
|
||||
}
|
||||
MessageType::TerminalAuthentication(t) => {
|
||||
let cnt = t.generate_reply(TerminalAuthentication::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
cnt.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
||||
}
|
||||
MessageType::LocationInformationReport(t) => {
|
||||
let cnt =
|
||||
t.generate_reply(LocationInformationReport::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
cnt.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
||||
}
|
||||
MessageType::TerminalHeartbeat(t) => {
|
||||
let cnt = t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
cnt.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
||||
}
|
||||
MessageType::TerminalLogout(t) => {
|
||||
let cnt = t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
cnt.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(cnt);
|
||||
}
|
||||
_ => {
|
||||
println!("no type");
|
||||
return None;
|
||||
match inmsg {
|
||||
Some(inmsg) => {
|
||||
let terminal_id = inmsg.header.get_raw_terminal_id().clone();
|
||||
match inmsg.content {
|
||||
MessageType::TerminalRegistration(t) => {
|
||||
let content =
|
||||
t.generate_reply(terminal_id.into(), inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
TerminalRegistrationReply::ID,
|
||||
content.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::TerminalRegistrationReply(content);
|
||||
}
|
||||
MessageType::TerminalAuthentication(t) => {
|
||||
let content = t
|
||||
.generate_reply(TerminalAuthentication::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
content.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||
}
|
||||
MessageType::LocationInformationReport(t) => {
|
||||
let content = t.generate_reply(
|
||||
LocationInformationReport::ID,
|
||||
inmsg.header.serial_number,
|
||||
);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
content.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||
}
|
||||
MessageType::TerminalHeartbeat(t) => {
|
||||
let content =
|
||||
t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
content.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||
}
|
||||
MessageType::TerminalLogout(t) => {
|
||||
let content =
|
||||
t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
||||
reply.header.build(
|
||||
PlatformUniversalResponse::ID,
|
||||
content.to_raw().len(),
|
||||
terminal_id,
|
||||
);
|
||||
reply.content = MessageType::PlatformUniversalResponse(content);
|
||||
}
|
||||
_ => {
|
||||
println!("no type");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
reply.outbound_finalize();
|
||||
Some(reply)
|
||||
}
|
||||
|
||||
pub fn store(inmsg: &Message) {
|
||||
match inmsg.content {
|
||||
MessageType::LocationInformationReport(ref t) => {
|
||||
/*{
|
||||
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();
|
||||
}*/
|
||||
|
||||
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 store(inmsg: &Message) -> Option<DbLog> {
|
||||
let res = match inmsg.content {
|
||||
MessageType::LocationInformationReport(ref t) => Some(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(),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn outbound_finalize(&mut self) {
|
||||
@ -457,3 +448,22 @@ impl std::fmt::Display for MessageType {
|
||||
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