Skip to content

Commit

Permalink
v0.7: custom-sized context, serde & bincode
Browse files Browse the repository at this point in the history
  • Loading branch information
divi255 committed Feb 28, 2023
1 parent 5dd13df commit a831994
Showing 14 changed files with 222 additions and 505 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## Changelog

### v0.7.0

* ModbusContext has become a struct with custom context sizes with generic
constants
* Use ModbusContextSmall for small contexts and ModbusContextFull for big ones
* Removed built-in dump/restore methods (use serde, bincode or custom ones)

### v0.6.3

* Moved modbus constants into a `consts` module.
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rmodbus"
version = "0.6.4"
version = "0.7.0"
authors = ["Sergei S. <div@altertech.com>"]
edition = "2021"
license = "Apache-2.0"
@@ -24,11 +24,15 @@ path = "src/lib.rs"
ieee754 = "0.2.6"
fixedvec = { version = "0.2.4", optional = true }
heapless = { version = "0.7.13", optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
serde_arrays = { version = "0.1.0", optional = true }
bincode = { version = "2.0.0-rc.2", optional = true }

[features]
default = ["std", "fullcontext"]
default = ["std"]
std = []
fullcontext = []
with_serde = ["serde", "serde_arrays"]
with_bincode = ["bincode"]

[dev-dependencies]
rand = "0.7.3"
@@ -41,7 +45,7 @@ spin = "0.5.2"
[[example]]
name = "app"
path = "examples/app.rs"
required-features = ["std"]
required-features = ["std", "with_bincode"]

[[example]]
name = "tcpserver"
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ VERSION=$(shell grep ^version Cargo.toml|cut -d\" -f2)

all: test

test: test-std test-nostd test-nostd-smallcontext
test: test-std test-nostd

test-std:
cargo test --all-features -- --test-threads=1 --nocapture
84 changes: 60 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -46,12 +46,12 @@ use lazy_static::lazy_static;
use std::sync::RwLock;
use rmodbus::{
server::{context::ModbusContext, ModbusFrame},
server::{context::ModbusContextFull, ModbusFrame},
ModbusFrameBuf, ModbusProto,
};
lazy_static! {
pub static ref CONTEXT: RwLock<ModbusContext> = RwLock::new(ModbusContext::new());
pub static ref CONTEXT: RwLock<ModbusContextFull> = RwLock::new(ModbusContextFull::new());
}
pub fn tcpserver(unit: u8, listen: &str) {
@@ -124,10 +124,17 @@ when required and only for a short period time.
A simple PLC example:

```rust,ignore
use std::error::Error;
use std::fs::File;
use std::io::{Write};
use std::io::{Read, Write};
use rmodbus::server::context::ModbusContext;
use rmodbus::server::context::ModbusContextFull;
#[path = "servers/tcp.rs"]
mod srv;
// put 1 to holding register 1500 to save current context to /tmp/plc1.dat
// if the file exists, context will be loaded at the next start
fn looping() {
println!("Loop started");
@@ -164,22 +171,37 @@ fn looping() {
}
}
fn save(fname: &str, ctx: &ModbusContext) -> Result<(), std::io::Error> {
let mut file = match File::create(fname) {
Ok(v) => v,
Err(e) => return Err(e),
};
for i in ctx.iter() {
match file.write(&[i]) {
Ok(_) => {}
Err(e) => return Err(e),
}
}
match file.sync_all() {
Ok(_) => {}
Err(e) => return Err(e),
fn save(fname: &str, ctx: &ModbusContextFull) -> Result<(), Box<dyn Error>> {
let config = bincode::config::standard();
let mut file = File::create(fname)?;
file.write(&bincode::encode_to_vec(ctx, config)?)?;
file.sync_all()?;
Ok(())
}
fn load(fname: &str, ctx: &mut ModbusContextFull) -> Result<(), Box<dyn Error>> {
let config = bincode::config::standard();
let mut file = File::open(fname)?;
let mut data: Vec<u8> = Vec::new();
file.read_to_end(&mut data)?;
(*ctx, _) = bincode::decode_from_slice(&data, config)?;
Ok(())
}
fn main() {
// read context
let unit_id = 1;
{
let mut ctx = srv::CONTEXT.write().unwrap();
let _ = load(&"/tmp/plc1.dat", &mut ctx).map_err(|_| {
eprintln!("warning: no saved context");
});
}
return Ok(());
use std::thread;
thread::spawn(move || {
srv::tcpserver(unit_id, "localhost:5502");
});
looping();
}
```

@@ -200,12 +222,26 @@ rmodbus = { version = "*", default-features = false, features = ["fullcontext"]
## Small context

The full Modbus context has 10000 registers of each type, which requires 60000
bytes total. For systems with small RAM amount it is possible to reduce the
context size to 1000 registers of each type (6000 bytes) by not enabling the
`fullcontext` feature:
bytes total. For systems with small RAM amount there is a pre-defined small
context with 1000 registers:

```toml
rmodbus = { version = "*", default-features = false }
```rust
use rmodbus::server::context::ModbusContextSmall;
```

## Custom-sized context

Starting from the version 0.7, it is allowed to define context of any size,
using generic constants. The generic constants order is: coils, discretes,
inputs, holdings.

E.g. let us define a context for 128 coils, 16 discretes, 0 inputs and 100
holdings:

```rust
use rmodbus::server::context::ModbusContext;

let context = ModbusContext::<128, 16, 0, 100>::new();
```

## Vectors
43 changes: 14 additions & 29 deletions examples/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::error::Error;
use std::fs::File;
use std::io::{Read, Write};

use rmodbus::server::context::ModbusContext;
use rmodbus::server::context::ModbusContextFull;

#[path = "servers/tcp.rs"]
mod srv;
@@ -44,37 +45,21 @@ fn looping() {
}
}

fn save(fname: &str, ctx: &ModbusContext) -> Result<(), std::io::Error> {
let mut file = match File::create(fname) {
Ok(v) => v,
Err(e) => return Err(e),
};
for i in ctx.iter() {
match file.write(&[i]) {
Ok(_) => {}
Err(e) => return Err(e),
}
}
match file.sync_all() {
Ok(_) => {}
Err(e) => return Err(e),
}
return Ok(());
fn save(fname: &str, ctx: &ModbusContextFull) -> Result<(), Box<dyn Error>> {
let config = bincode::config::standard();
let mut file = File::create(fname)?;
file.write(&bincode::encode_to_vec(ctx, config)?)?;
file.sync_all()?;
Ok(())
}

fn load(fname: &str, ctx: &mut ModbusContext) -> Result<(), std::io::Error> {
let mut file = match File::open(fname) {
Ok(v) => v,
Err(e) => return Err(e),
};
fn load(fname: &str, ctx: &mut ModbusContextFull) -> Result<(), Box<dyn Error>> {
let config = bincode::config::standard();
let mut file = File::open(fname)?;
let mut data: Vec<u8> = Vec::new();
match file.read_to_end(&mut data) {
Ok(_) => {}
Err(e) => return Err(e),
}
let mut writer = ctx.create_writer();
writer.write_bulk(data.as_slice()).unwrap();
return Ok(());
file.read_to_end(&mut data)?;
(*ctx, _) = bincode::decode_from_slice(&data, config)?;
Ok(())
}

fn main() {
4 changes: 2 additions & 2 deletions examples/servers/ascii.rs
Original file line number Diff line number Diff line change
@@ -8,12 +8,12 @@ use std::sync::RwLock;

use rmodbus::{
generate_ascii_frame, guess_request_frame_len, parse_ascii_frame,
server::{context::ModbusContext, ModbusFrame},
server::{context::ModbusContextFull, ModbusFrame},
ModbusFrameBuf, ModbusProto,
};

lazy_static! {
pub static ref CONTEXT: RwLock<ModbusContext> = RwLock::new(ModbusContext::new());
pub static ref CONTEXT: RwLock<ModbusContextFull> = RwLock::new(ModbusContextFull::new());
}

pub fn asciiserver(unit: u8, port: &str) {
4 changes: 2 additions & 2 deletions examples/servers/rtu.rs
Original file line number Diff line number Diff line change
@@ -7,12 +7,12 @@ use lazy_static::lazy_static;
use std::sync::RwLock;

use rmodbus::{
server::{context::ModbusContext, ModbusFrame},
server::{context::ModbusContextFull, ModbusFrame},
ModbusFrameBuf, ModbusProto,
};

lazy_static! {
pub static ref CONTEXT: RwLock<ModbusContext> = RwLock::new(ModbusContext::new());
pub static ref CONTEXT: RwLock<ModbusContextFull> = RwLock::new(ModbusContextFull::new());
}

pub fn rtuserver(unit: u8, port: &str) {
4 changes: 2 additions & 2 deletions examples/servers/tcp.rs
Original file line number Diff line number Diff line change
@@ -7,12 +7,12 @@ use lazy_static::lazy_static;
use std::sync::RwLock;

use rmodbus::{
server::{context::ModbusContext, ModbusFrame},
server::{context::ModbusContextFull, ModbusFrame},
ModbusFrameBuf, ModbusProto,
};

lazy_static! {
pub static ref CONTEXT: RwLock<ModbusContext> = RwLock::new(ModbusContext::new());
pub static ref CONTEXT: RwLock<ModbusContextFull> = RwLock::new(ModbusContextFull::new());
}

pub fn tcpserver(unit: u8, listen: &str) {
4 changes: 2 additions & 2 deletions examples/servers/udp.rs
Original file line number Diff line number Diff line change
@@ -5,12 +5,12 @@ use lazy_static::lazy_static;
use std::sync::RwLock;

use rmodbus::{
server::{context::ModbusContext, ModbusFrame},
server::{context::ModbusContextFull, ModbusFrame},
ModbusFrameBuf, ModbusProto,
};

lazy_static! {
pub static ref CONTEXT: RwLock<ModbusContext> = RwLock::new(ModbusContext::new());
pub static ref CONTEXT: RwLock<ModbusContextFull> = RwLock::new(ModbusContextFull::new());
}

pub fn udpserver(unit: u8, listen: &str) {
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ pub fn parse_ascii_frame(
frame_buf: &mut ModbusFrameBuf,
frame_pos: u8,
) -> Result<u8, ErrorKind> {
let mut data_pos = if data[0] == 58 { 1 } else { 0 };
let mut data_pos = usize::from(data[0] == 58);
let mut cpos = frame_pos;
while data_pos < data_len {
if cpos == 255 {
Loading

0 comments on commit a831994

Please sign in to comment.