Check, format and test / build (push) Failing after 5s
- request() now reads exactly the expected response length with read_exact instead of a single non-guaranteed fill_buf, fixing a latent bug where a response split across multiple TCP packets could be mistaken for malformed data; the per-method length checks are now redundant and were removed - sequence_number wraps explicitly via wrapping_add instead of relying on implicit (and debug-vs-release inconsistent) overflow behavior after 65536 requests - get_name validates UTF-8 strictly instead of silently lossy-decoding invalid bytes; on invalid data it now returns the FromUtf8Error itself (byte-level \0 trimming moved after the UTF-8 check), letting the caller choose to fail or fall back to a lossy rendering - removed the unused Default derive from Clock and Record - removed npm from the CI image (no JS/Node in this repo), fixed a typo in a CI step name - fixed the Cargo.toml license field to the actual SPDX identifier (AGPL-3.0-or-later, per the notice at the end of LICENSE) instead of the free-text "GNU AGPLv3.0" Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
69 lines
2.5 KiB
Markdown
69 lines
2.5 KiB
Markdown
<div align="center">
|
|
|
|
# R701 🕰️
|
|
|
|
[](https://www.rust-lang.org)
|
|
[](https://claude.com/product/claude-code)
|
|
[](https://choosealicense.com/licenses/agpl-3.0)
|
|
[](https://buymeacoffee.com/nicolabelluti)
|
|
<br>
|
|
[](https://git.nicolabelluti.me/nicolabelluti/r701/actions/?workflow=check-format-and-test.yaml)
|
|
|
|
</div><br>
|
|
|
|
> A reverse-engineered library to communicate with the
|
|
> [R701](https://ipsattendant.it/rilevatore-presenze-r701/) by [I.P.S.
|
|
> Informatica](https://ipsinformatica.info/), written in Rust
|
|
|
|
If you want to read about this reverse engineering attempt, check out
|
|
<https://nicolabelluti.me/series/attendance-reader/>.
|
|
|
|
## Library Usage
|
|
|
|
1. Add the library to you project:
|
|
|
|
```shell
|
|
cargo add r701 --git https://git.nicolabelluti.me/nicolabelluti/r701.git
|
|
```
|
|
|
|
2. Use the library in you project:
|
|
|
|
This library is async and built on [tokio](https://tokio.rs); add it to your
|
|
own `[dependencies]` (e.g. `tokio = { version = "1", features =
|
|
["rt-multi-thread", "macros"] }`) to run the example below.
|
|
|
|
```rust
|
|
use r701::R701;
|
|
use tokio_stream::StreamExt;
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let r701 = R701::connect("127.0.0.1:5005").await.unwrap();
|
|
let mut record_stream = r701.into_record_iter().await.unwrap();
|
|
let mut id = 0;
|
|
|
|
println!("No\tMchn\tEnNo\t\tName\t\tMode\tIOMd\tDateTime\t");
|
|
while let Some(record) = record_stream.next().await {
|
|
let record = record.unwrap();
|
|
let name = match r701.get_name(record.employee_id).await.unwrap() {
|
|
Some(Ok(name)) => name,
|
|
Some(Err(error)) => String::from_utf8_lossy(error.as_bytes()).into_owned(),
|
|
None => format!("user #{}", record.employee_id),
|
|
};
|
|
|
|
println!(
|
|
"{:0>6}\t{}\t{:0>9}\t{: <10}\t{}\t{}\t{}",
|
|
id + 1,
|
|
1,
|
|
record.employee_id,
|
|
name,
|
|
35,
|
|
record.clock as u8,
|
|
record.datetime.format("%Y/%m/%d %H:%M:%S"),
|
|
);
|
|
|
|
id += 1;
|
|
}
|
|
}
|
|
```
|