initial commit after modification of prusatool-rs to make libraryish
This commit is contained in:
commit
ea6fd69abc
4 changed files with 291 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "prusalib-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = { version = "0.12.23", features = ["blocking"] }
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
serde_json = "1.0.143"
|
||||||
|
thiserror = "2.0.16"
|
||||||
|
toml = "0.9.5"
|
||||||
|
uuid = { version = "1.18.1", features = ["v4"] }
|
||||||
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
mod prusa;
|
||||||
277
src/prusa.rs
Normal file
277
src/prusa.rs
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::fs;
|
||||||
|
use thiserror::Error;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Telemetry {
|
||||||
|
#[serde(rename = "temp-bed")]
|
||||||
|
temp_bed: f32,
|
||||||
|
#[serde(rename = "temp-nozzle")]
|
||||||
|
temp_nozzle: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct State {
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Info {
|
||||||
|
telemetry: Telemetry,
|
||||||
|
// temperature: Temperature,
|
||||||
|
state: State,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct StorageInfo {
|
||||||
|
storage_list: Vec<Storage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Storage {
|
||||||
|
path: String,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct PrusaConfig {
|
||||||
|
api_key: String,
|
||||||
|
ip_addr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Prusa {
|
||||||
|
config: PrusaConfig,
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prusa {
|
||||||
|
pub fn new(config: PrusaConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
client: Client::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn new_from_file(filepath: &str) -> Result<Self, Error> {
|
||||||
|
let file_data = fs::read_to_string(filepath)?;
|
||||||
|
let config: PrusaConfig = toml::from_str(&file_data)?;
|
||||||
|
Ok(Self::new(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_info(&self) -> Result<Info, Error> {
|
||||||
|
let api_target = "/api/printer";
|
||||||
|
let resp = self.client.get(format!("http://{}{api_target}", self.config.ip_addr))
|
||||||
|
.header("X-Api-Key", &self.config.api_key)
|
||||||
|
.send()?;
|
||||||
|
let text = resp.text()?;
|
||||||
|
// println!("{text}");
|
||||||
|
let info: Info = serde_json::from_str(&text)?;
|
||||||
|
// println!("{info:?}");
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO return this into a specific type
|
||||||
|
pub fn get_storage_info(&self) -> Result<StorageInfo, Error> {
|
||||||
|
let api_target = "/api/v1/storage";
|
||||||
|
let resp = self.client.get(format!("http://{}{api_target}", self.config.ip_addr))
|
||||||
|
.header("X-Api-Key", &self.config.api_key)
|
||||||
|
.send()?;
|
||||||
|
let text = resp.text()?;
|
||||||
|
// println!("{text}");
|
||||||
|
let info: StorageInfo = serde_json::from_str(&text)?;
|
||||||
|
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prusalink load file schema:
|
||||||
|
// content:
|
||||||
|
// application/octet-stream:
|
||||||
|
// schema:
|
||||||
|
// type: string
|
||||||
|
// format: binary
|
||||||
|
|
||||||
|
|
||||||
|
// Need parameters
|
||||||
|
/*
|
||||||
|
parameters:
|
||||||
|
- in: header
|
||||||
|
name: Content-Length
|
||||||
|
description: Length of file to upload
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
example: 101342
|
||||||
|
- in: header
|
||||||
|
name: Content-Type
|
||||||
|
description: Type of uploaded media
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
default: application/octet-stream
|
||||||
|
- in: header
|
||||||
|
name: Print-After-Upload
|
||||||
|
description: Whether to start printing the file after upload
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: ?0=False, ?1=True, according RFC8941/3.3.6
|
||||||
|
enum: [ "?0", "?1" ]
|
||||||
|
default: "?0"
|
||||||
|
- in: header
|
||||||
|
name: Overwrite
|
||||||
|
description: Whether to overwrite already existing files
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: ?0=False, ?1=True, according RFC8941/3.3.6
|
||||||
|
enum: ["?0", "?1"]
|
||||||
|
default: "?0"
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: async
|
||||||
|
pub fn try_load_file(&self, filepath: &str) -> Result<Uuid, Error> {
|
||||||
|
let data = fs::read(filepath)?;
|
||||||
|
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
// TODO: Allow storage selection
|
||||||
|
// For now, assume that we're using only USB:
|
||||||
|
let api_target = format!("/api/v1/files/usb/{uuid}.bgcode");
|
||||||
|
let resp = self.client.put(format!("http://{}{api_target}", self.config.ip_addr))
|
||||||
|
.header("X-Api-Key", &self.config.api_key)
|
||||||
|
.header("Content-Type", "application/octet-stream")
|
||||||
|
.header("Content-Length", data.len())
|
||||||
|
.header("Print-After-Upload", "0")
|
||||||
|
.header("Overwrite", "0")
|
||||||
|
.body(data)
|
||||||
|
.timeout(Duration::from_secs(60))
|
||||||
|
.send()?;
|
||||||
|
let text = resp.text()?;
|
||||||
|
println!("{text}");
|
||||||
|
|
||||||
|
// todo!("Implement UUID Handling");
|
||||||
|
Ok(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// /api/v1/files/{storage}/{path}:
|
||||||
|
//
|
||||||
|
// post:
|
||||||
|
/* summary: Start print of file if there's no print job running
|
||||||
|
description: Body is ignored
|
||||||
|
requestBody:
|
||||||
|
required: false
|
||||||
|
content: {}
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: No Content
|
||||||
|
401:
|
||||||
|
$ref: "#/components/responses/Unauthorized"
|
||||||
|
404:
|
||||||
|
$ref: "#/components/responses/NotFound"
|
||||||
|
409:
|
||||||
|
$ref: "#/components/responses/Conflict"
|
||||||
|
head:
|
||||||
|
summary: file presence and state check
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: OK
|
||||||
|
headers:
|
||||||
|
Read-Only:
|
||||||
|
description: Whether the file or storage is read-only
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
|
Currently-Printed:
|
||||||
|
description: Whether this file is currently being printed
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: async
|
||||||
|
pub fn try_print_file(&self, id: &Uuid) -> Result<(), Error> {
|
||||||
|
// /api/v1/files/{storage}/{path}
|
||||||
|
let api_target = format!("/api/v1/files/usb/{id}.bgcode");
|
||||||
|
let resp = self.client.post(format!("http://{}{api_target}", self.config.ip_addr))
|
||||||
|
.header("X-Api-Key", &self.config.api_key)
|
||||||
|
.header("Content-Type", "application/octet-stream")
|
||||||
|
.send()?;
|
||||||
|
let text = resp.text()?;
|
||||||
|
println!("{text}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: poorly named, should be something else so as not to confuse with print operations
|
||||||
|
pub fn print_info(&self) -> Result<(), Error> {
|
||||||
|
let info = self.get_info()?;
|
||||||
|
println!("\n--------------------");
|
||||||
|
println!("STATE:\t{}", info.state.text);
|
||||||
|
println!("NOZZLE:\t{}", info.telemetry.temp_nozzle);
|
||||||
|
println!("BED:\t{}", info.telemetry.temp_bed);
|
||||||
|
println!("--------------------\n");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_storage_info(&self) -> Result<(), Error> {
|
||||||
|
let info = self.get_storage_info()?;
|
||||||
|
for (index, storage) in info.storage_list.iter().enumerate() {
|
||||||
|
println!("\n------------------");
|
||||||
|
println!("STORAGE: {index}");
|
||||||
|
println!(" PATH:\t{}", storage.path);
|
||||||
|
println!(" NAME:\t{}", storage.name);
|
||||||
|
println!("--------------------\n");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("File error")]
|
||||||
|
FileError(#[from] std::io::Error),
|
||||||
|
#[error("Toml Parse Error")]
|
||||||
|
TomlError(#[from] toml::de::Error),
|
||||||
|
#[error("HTTP(S) Error")]
|
||||||
|
HttpError(#[from] reqwest::Error),
|
||||||
|
#[error("Serde JSON Error")]
|
||||||
|
JsonError(#[from] serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fn main() -> Result<(), Error> {
|
||||||
|
// let cli = Cli::parse();
|
||||||
|
// let file_data = fs::read_to_string(cli.printer_config)?;
|
||||||
|
// let config: Config = toml::from_str(&file_data)?;
|
||||||
|
// // println!("{:?}", config);
|
||||||
|
//
|
||||||
|
// match config.printer.model.as_str() {
|
||||||
|
// "prusa-mk4" => {
|
||||||
|
// let prusa = Prusa::new(&config.printer.api_key, &config.printer.ip_addr);
|
||||||
|
// match cli.command {
|
||||||
|
// Command::Info => {
|
||||||
|
// prusa.print_info()?;
|
||||||
|
// prusa.print_storage_info()?;
|
||||||
|
// },
|
||||||
|
// // Should generate UUID for the filename:
|
||||||
|
// Command::Load {filepath, print_immediately} => {
|
||||||
|
// let uuid = prusa.try_load_file(&filepath)?;
|
||||||
|
// if print_immediately {
|
||||||
|
// prusa.try_print_file(&uuid)?;
|
||||||
|
// }
|
||||||
|
// println!("Loaded as UUID:\n{uuid}");
|
||||||
|
// },
|
||||||
|
// Command::Print {file_id} => prusa.try_print_file(&file_id)?,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// _ => println!("Unrecognized printer type")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue