Removed DLL

Added All functions from DLL into HarmonyLinkServer
This commit is contained in:
Jordon Brooks 2023-06-22 01:40:59 +01:00
parent 53773040d6
commit 0d5497be1c
27 changed files with 2146 additions and 64 deletions

18
src/api/all_info.rs Normal file
View file

@ -0,0 +1,18 @@
use crate::docking;
use crate::battery;
use crate::os;
use crate::version;
use super::structs::Allinfo;
/* This will query all the modules and return all the combined data */
pub fn get_all_info() -> Result<Allinfo, Box<dyn std::error::Error>> {
let mut all_info = Allinfo::new();
all_info.dock = docking::stats::get_dock_info()?;
all_info.battery = battery::stats::get_battery_info()?;
all_info.os = os::stats::get_os()?;
all_info.version = version::info::Version::get();
Ok(all_info)
}

65
src/api/endpoints.rs Normal file
View file

@ -0,0 +1,65 @@
use actix_web::{HttpResponse, get};
use crate::docking;
use crate::version;
use crate::battery;
#[get("/are_you_there")]
pub async fn index() -> HttpResponse {
HttpResponse::Ok().body("yes")
}
#[get("/all_info")]
pub async fn get_all_info() -> HttpResponse {
match crate::api::all_info::get_all_info() {
Ok(info) => {
HttpResponse::Ok().json(&info)
},
Err(err) => {
HttpResponse::InternalServerError().body(format!("Failed to get device info: {}", err))
}
}
}
#[get("/dock_info")]
pub async fn get_dock_info() -> HttpResponse {
match docking::stats::get_dock() {
Ok(info) => {
HttpResponse::Ok().json(&info)
},
Err(err) => {
HttpResponse::InternalServerError().body(format!("Failed to get dock info: {}", err))
}
}
}
#[get("/os_info")]
pub async fn get_os_info() -> HttpResponse {
match crate::os::stats::get_os() {
Ok(info) => {
HttpResponse::Ok().json(&info)
},
Err(err) => {
HttpResponse::InternalServerError().body(format!("Failed to get OS info: {}", err))
}
}
}
#[get("/battery_info")]
pub async fn get_battery_info() -> HttpResponse {
match battery::stats::get_battery_info() {
Ok(info) => {
HttpResponse::Ok().json(&info)
},
Err(err) => {
HttpResponse::InternalServerError().body(format!("Failed to get battery info: {}", err))
}
}
}
#[get("/version_info")]
pub async fn get_version_info() -> HttpResponse {
HttpResponse::Ok().json(&version::info::Version::get())
}

4
src/api/mod.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod server;
mod endpoints;
mod structs;
mod all_info;

26
src/api/server.rs Normal file
View file

@ -0,0 +1,26 @@
use actix_web::HttpServer;
use crate::api::endpoints::*;
pub async fn start_actix_web(port: u16) -> std::io::Result<()> {
//std::env::set_var("RUST_LOG", "debug");
//std::env::set_var("RUST_BACKTRACE", "1");
println!("Starting webserver on 127.0.0.1:{}", port);
HttpServer::new(move || {
let logger = actix_web::middleware::Logger::default();
actix_web::App::new()
.wrap(logger)
.service(index)
.service(get_all_info)
.service(get_os_info)
.service(get_battery_info)
.service(get_version_info)
.service(get_dock_info)
})
.bind(("127.0.0.1", port))?
.run()
.await
}

20
src/api/structs.rs Normal file
View file

@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};
use crate::{os, battery, docking::{self, structs::DockInfo}, version};
#[derive(Deserialize, Serialize)]
pub struct Allinfo {
pub os: os::structs::OSInfo,
pub battery: battery::structs::BatteryInfo,
pub dock: docking::structs::DockInfo,
pub version: version::info::Version
}
impl Allinfo {
pub fn new() -> Allinfo {
Allinfo { os: os::structs::OSInfo::new(),
battery: battery::structs::BatteryInfo::new(),
dock: DockInfo::new(),
version: version::info::Version::get()
}
}
}

2
src/battery/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod stats;
pub mod structs;

38
src/battery/stats.rs Normal file
View file

@ -0,0 +1,38 @@
use crate::battery::structs::ChargingStatus;
use super::structs::BatteryInfo;
pub fn get_battery_info() -> Result<BatteryInfo, Box<dyn std::error::Error>> {
let mut battery_info = BatteryInfo { has_battery: false, battery_percent: 0, charging_status: ChargingStatus::UNKNOWN };
let manager = battery::Manager::new().unwrap();
battery_info.has_battery = manager.batteries().unwrap().count() > 0;
if !battery_info.has_battery {
return Ok(battery_info);
}
for (idx, battery) in manager.batteries()?.enumerate() {
let battery = battery?;
let state = battery.state();
let energy = battery.energy();
let full_energy = battery.energy_full();
let state_of_charge = (energy / full_energy).get::<battery::units::ratio::percent>();
println!("Battery #{}:", idx);
println!("Charging status: {:?}", state);
println!("Charge level: {:.2}%", state_of_charge);
battery_info.battery_percent = state_of_charge.round() as i8;
battery_info.charging_status = match state {
battery::State::Charging => ChargingStatus::Charging,
battery::State::Discharging => ChargingStatus::Battery,
battery::State::Empty => ChargingStatus::Battery,
battery::State::Full => ChargingStatus::Battery,
_ => ChargingStatus::UNKNOWN,
}
}
Ok(battery_info)
}

25
src/battery/structs.rs Normal file
View file

@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, PartialEq)]
pub enum ChargingStatus {
Charging,
Battery,
UNKNOWN,
}
#[derive(Deserialize, Serialize)]
pub struct BatteryInfo {
pub has_battery: bool,
pub battery_percent: i8,
pub charging_status: ChargingStatus,
}
impl BatteryInfo {
pub fn new() -> BatteryInfo {
BatteryInfo {
has_battery: false,
battery_percent: 0,
charging_status: ChargingStatus::UNKNOWN
}
}
}

2
src/docking/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod stats;
pub mod structs;

86
src/docking/stats.rs Normal file
View file

@ -0,0 +1,86 @@
use std::{io::BufReader, fs::File, collections::HashSet};
use crate::{battery::{stats::get_battery_info, structs::ChargingStatus}, USE_FALLBACK_DOCK_DETECTION};
use super::structs::{Dock, DockInfo};
/* This will get the current dock info. */
pub fn get_dock_info() -> Result<DockInfo, Box<dyn std::error::Error>> {
let mut dock = DockInfo::new();
dock.dock_info = get_dock()?;
dock.is_docked = dock.dock_info.brand == String::new() && dock.dock_info.model == String::new();
/*
This code will manually detect a dock if it wasn't automatically picked up by get_dock().
The code currently doesnt work. To manually detect a dock we will detect the presence of
a battery, charging of the handheld, and eventually when I find a cross-platform create,
it will also detect the presence of an external monitor (will most likely only work on a
Steam Deck for now)
*/
if USE_FALLBACK_DOCK_DETECTION {
if !dock.is_docked {
dock.fallback_detection = true;
let battery_info = get_battery_info()?;
if battery_info.has_battery && battery_info.charging_status == ChargingStatus::Charging {
}
}
}
Ok(dock)
}
/* Reads the dock_models.json file and returns a vector of structs with the data */
pub fn read_dock_models_from_file() -> Result<Vec<Dock>, Box<dyn std::error::Error>> {
let file = File::open("Resources/dock_models.json")?;
let reader = BufReader::new(file);
let dock_models: Vec<Dock> = serde_json::from_reader(reader)?;
Ok(dock_models)
}
#[cfg(target_os = "linux")]
/* This will detect the dock model and brand. */
pub fn get_dock() -> Result<Dock, Box<dyn std::error::Error>> {
let devices = rusb::devices()?;
let dock_models = read_dock_models_from_file()?;
for dock_model in dock_models {
let mut found_components = HashSet::new();
for device in devices.iter() {
let device_desc = device.device_descriptor()?;
let device_id = (device_desc.vendor_id(), device_desc.product_id());
// Check if the device is one of the components of the dock.
if dock_model.usb_ids.contains(&[device_desc.vendor_id(), device_desc.product_id()]) {
found_components.insert(device_id);
println!("(get_dock) Detected: {}", serde_json::to_string_pretty(&device_id)?);
}
}
if found_components.len() == dock_model.usb_ids.len() {
println!("(get_dock) All components detected for {}", serde_json::to_string_pretty(&dock_model)?);
return Ok(Dock {
model: dock_model.model.clone(),
brand: dock_model.brand.clone(),
usb_ids: dock_model.usb_ids.clone()
});
}
}
Ok(Dock::new())
}
#[cfg(target_os = "macos")]
/* This will detect the dock model and brand. */
pub fn get_dock() -> Result<Dock, Box<dyn std::error::Error>> {
Ok(Dock::new())
//Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Incorrect OS")))
}
/* This will detect the dock model and brand. */
#[cfg(target_os = "windows")]
pub fn get_dock() -> Result<Dock, Box<dyn std::error::Error>> {
Ok(Dock::new())
}

33
src/docking/structs.rs Normal file
View file

@ -0,0 +1,33 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
pub struct Dock {
pub brand: String, // ex: JSAUX
pub model: String, // ex: HB0603
pub usb_ids: Vec<[u16; 2]>,
}
impl Dock {
pub fn new() -> Dock {
Dock { brand: String::new(),
model: String::new(),
usb_ids: vec![],
}
}
}
#[derive(Deserialize, Serialize)]
pub struct DockInfo {
pub dock_info: Dock,
pub is_docked: bool,
pub fallback_detection: bool,
}
impl DockInfo {
pub fn new() -> DockInfo {
DockInfo { dock_info: Dock::new(),
is_docked: false,
fallback_detection: false
}
}
}

View file

@ -1,34 +1,36 @@
extern crate libloading;
mod battery;
mod docking;
mod os;
mod version;
use version::info::Version;
mod api;
static PORT: u16 = 9000;
static USE_FALLBACK_DOCK_DETECTION: bool = false;
fn main() {
// Use `cfg!` macro to detect OS
let lib_path = if cfg!(target_os = "windows") {
"harmony_link_core.dll"
} else if cfg!(target_os = "linux") {
"libharmony_link_core.so"
} else {
eprintln!("Unsupported OS");
return;
};
let version_info = Version::get();
let lib = unsafe { match libloading::Library::new(lib_path) {
Ok(lib) => lib,
Err(err) => {
eprintln!("Error loading dynamic library: {}", err);
return;
},
}
};
println!("Version: {}", version_info.version);
println!("Build Timestamp: {}", version_info.build_timestamp);
println!("Git Branch: {}", version_info.git_branch);
println!("Git Describe: {}", version_info.git_describe);
println!("Git Commit Timestamp: {}", version_info.git_commit_timestamp);
println!("Debug Build: {}", version_info.debug);
println!("\n\n");
unsafe {
let func: libloading::Symbol<unsafe extern "C" fn()> = match lib.get(b"start") {
Ok(func) => func,
Err(err) => {
eprintln!("Error finding function in dynamic library: {}", err);
return;
}
};
func();
}
println!("HarmonyLink ©️ Jordon Brooks 2023");
let sys = actix_web::rt::System::new();
sys.block_on(async {
let result = api::server::start_actix_web (PORT).await;
match result {
Ok(_) => println!("Webserver started successfully."),
Err(e) => println!("Error starting actix_web: {}", e),
}
});
}

2
src/os/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod stats;
pub mod structs;

45
src/os/stats.rs Normal file
View file

@ -0,0 +1,45 @@
use super::structs::{OSInfo, Platform, Architecture};
pub fn get_platform() -> Platform {
if cfg!(target_os = "windows") {
Platform::WINDOWS
} else if cfg!(target_os = "macos") {
Platform::MAC
} else if cfg!(target_os = "linux") {
Platform::LINUX
} else {
Platform::UNKNOWN
}
}
pub fn get_os() -> Result<OSInfo, Box<dyn std::error::Error>> {
let mut temp = OSInfo::new();
let info = os_info::get();
temp.platform = get_platform();
temp.name = match info.codename() {
Some(s) => s.to_string(),
_ => String::new(),
};
temp.version = info.version().to_string();
if temp.name == String::new() {
temp.name = match info.edition() {
Some(s) => s.to_string(),
_ => String::new(),
};
}
temp.bits = match info.bitness()
{
os_info::Bitness::X32 => Architecture::X86,
os_info::Bitness::X64 => Architecture::X86_64,
_ => Architecture::UNKNOWN,
};
Ok(temp)
}

35
src/os/structs.rs Normal file
View file

@ -0,0 +1,35 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
pub enum Platform {
WINDOWS = 0,
LINUX = 1,
MAC = 2,
UNKNOWN = 255
}
#[derive(Deserialize, Serialize)]
pub enum Architecture {
X86 = 0,
X86_64 = 1,
UNKNOWN = 255,
}
#[derive(Deserialize, Serialize)]
pub struct OSInfo {
pub platform: Platform, // Windows, Mac, Linux
pub name: String, // "Windows 11 2306", "Ubuntu 22.04 LTS"
pub version: String, // 2306, 22.04
pub bits: Architecture // 32, 64
}
impl OSInfo {
pub fn new() -> OSInfo {
OSInfo {
platform: Platform::UNKNOWN,
name: String::new(),
version: String::new(),
bits: Architecture::UNKNOWN
}
}
}

32
src/version/info.rs Normal file
View file

@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
pub struct Version {
pub build_timestamp: String,
pub git_branch: String,
pub git_describe: String,
pub git_commit_timestamp: String,
pub debug: bool,
pub version: String,
pub version_major: i32,
pub version_minor: i32,
pub version_patch: i32,
pub version_pre: String
}
impl Version {
pub fn get() -> Version {
Version {
build_timestamp: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
git_branch: env!("VERGEN_GIT_BRANCH").to_string(),
git_describe: env!("VERGEN_GIT_DESCRIBE").to_string(),
git_commit_timestamp: env!("VERGEN_GIT_COMMIT_TIMESTAMP").to_string(),
debug: env!("VERGEN_CARGO_DEBUG").parse().unwrap(),
version: env!("CARGO_PKG_VERSION").to_string(),
version_major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
version_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
version_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
version_pre: env!("CARGO_PKG_VERSION_PRE").parse().unwrap()
}
}
}

1
src/version/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod info;