Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Custom Defined Function Codes (FC 65..=72, 100..=110) #134

Open
wants to merge 110 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
2723204
feat: implement send/receive custom buffer
thr2240 Dec 6, 2023
30463e6
Merge pull request #1 from thr2240/boris/custom-buffer
biancode Dec 6, 2023
3a07ec1
chore: fix client and server ports for Unix-based architectures with …
dkoehler-dev Jan 17, 2024
c9c7923
chore: Implement placeholder commands for read/write custom function …
dkoehler-dev Jan 18, 2024
ad140d6
feat: Implement request building for custom function code
dkoehler-dev Jan 18, 2024
bc84c5e
feat: Declare custom function code module
dkoehler-dev Jan 18, 2024
e42d375
feat: Add 'write custom function code' as function code
dkoehler-dev Jan 18, 2024
6288b90
feat: Implement 'write custom function code' functionality into the m…
dkoehler-dev Jan 18, 2024
284e8d9
feat: Implement 'write custom function code' in channel handler
dkoehler-dev Jan 18, 2024
d0b381f
feat: Implement 'write custom function code' in client example
dkoehler-dev Jan 18, 2024
fc3d8b4
feat: Implement 'write custom function code' in server request handler
dkoehler-dev Jan 18, 2024
10c81d9
feat: Implement 'write custom function code' in server handler & serv…
dkoehler-dev Jan 18, 2024
a232ef3
feat: Implement 'write custom function code' in frame handler
dkoehler-dev Jan 18, 2024
d6c7a31
feat: Add CustomFC type; Implement client request for CustomFC type
dkoehler-dev Jan 19, 2024
6d6d916
feat: Implement CustomFC Display trait
dkoehler-dev Jan 19, 2024
90744c1
feat: Implement CustomFC type in client-side request
dkoehler-dev Jan 19, 2024
38b3404
fix: Implement UnitId Display trait
dkoehler-dev Jan 22, 2024
fa71af4
chore: Replace write custom FC client request parameter
dkoehler-dev Jan 22, 2024
788be9f
fix: Implement BitIterator parse_all fn
dkoehler-dev Jan 22, 2024
1934f3d
fix: Implement missing documentation for CustomFunctionCode fn
dkoehler-dev Jan 22, 2024
476e5ea
chore: Set tracing level to DEBUG
dkoehler-dev Jan 22, 2024
ea4f641
fix: Implement customFC type in server-side request
dkoehler-dev Jan 22, 2024
debeaa1
feat: Implement custom function code parser
dkoehler-dev Jan 22, 2024
9a9eaf4
feat: Implement Serialize & Loggable traits for CustomFunctionCode
dkoehler-dev Jan 22, 2024
c1d5e68
fix: Replace Vec<u16> with a fixed-size array for now, to work around…
dkoehler-dev Jan 23, 2024
d71bf86
fix: Implement missing customFC request in server-side request handling
dkoehler-dev Jan 23, 2024
5bd36f1
fix: Implement correct CustomFunctionCode value parsing
dkoehler-dev Jan 24, 2024
bff9685
fix: Implement customFC length attribute
dkoehler-dev Jan 24, 2024
8954e36
feat: Improved server and client examples for custom function code fe…
dkoehler-dev Jan 24, 2024
2719e74
chore: Improve type naming of the custom function code feature
dkoehler-dev Jan 24, 2024
be8eba7
chore: Improve server and client examples for write custom function c…
dkoehler-dev Jan 24, 2024
6438e99
chore: Set server and client port in examples >1024 for non-root linu…
dkoehler-dev Jan 25, 2024
8ae0812
fix: Authorize Write Custom Function Code Request in server handler (…
dkoehler-dev Jan 25, 2024
3504b1d
fix: Correct logged offset server PDU TX values for the Custom Functi…
dkoehler-dev Jan 25, 2024
21413af
test: Add Custom Function Code feature parsing unit test
dkoehler-dev Jan 25, 2024
570aefb
test: Add Custom Function Code parsing unit test for invalid values
dkoehler-dev Jan 25, 2024
7479461
test: Add Custom Function Code serializing unit test
dkoehler-dev Jan 25, 2024
7248688
test: Add Custom Function Code integration test
dkoehler-dev Jan 25, 2024
116c97b
refactor: Improve client example script for the Custom Function Code …
dkoehler-dev Jan 26, 2024
51cd999
test: Add integration test for the Custom Function Code feature
dkoehler-dev Jan 26, 2024
e2c80ce
fix: Resolve immutable server handler value for the Custom Function C…
dkoehler-dev Jan 26, 2024
15dd7e6
refactor(client): code cleanup
dkoehler-dev Jan 26, 2024
6e4f068
refactor(server): code cleanup
dkoehler-dev Jan 26, 2024
28d4381
refactor(parse): code cleanup
dkoehler-dev Jan 26, 2024
81f6188
docs(write_custom_function_code): Add documentation for write custom …
dkoehler-dev Jan 26, 2024
4a9ce9b
Merge pull request #3 from dkoehler-dev/development
biancode Jan 27, 2024
1926d68
chore(send_cfc): Rename CFC README.me
dkoehler-dev Jan 29, 2024
cf2a350
fix(examples): Adjust TCP & TLS port for non-root Unix users
dkoehler-dev Jan 29, 2024
fcb4beb
refactor(custom_fc): Rename WriteCustomFC to SendCustomFC
dkoehler-dev Jan 29, 2024
7054730
refactor(custom_fc): Remove SendCustomBuffer functionality (PR 2 on I…
dkoehler-dev Jan 29, 2024
076f66c
feat(custom examples): add custom client and server examples for CFC6…
dkoehler-dev Feb 1, 2024
0e02219
feat(cli): add CLI tool to call CFC69 (CustomFunctionCode) via the CL…
dkoehler-dev Feb 1, 2024
1f9a046
refactor: move cli functionality
dkoehler-dev Feb 12, 2024
8d8441f
feat(custom client example): Extend the custom client example for the…
dkoehler-dev Feb 14, 2024
4dc5972
refactor(send_custom_fc): change CFC69 CustomFC implementation to sup…
dkoehler-dev Feb 14, 2024
97cbef1
refactor(send_custom_fc): implement parsing and serializing logic for…
dkoehler-dev Feb 14, 2024
5355f24
fix(custom_fc): fix serialize error for CFC request
dkoehler-dev Feb 15, 2024
7a150c2
test(parse): Extend CFC69 parse unit test cases
dkoehler-dev Feb 15, 2024
f696091
test(serialize): extend CFC69 serialize unit test cases
dkoehler-dev Feb 15, 2024
4c9510f
test(parse): extend CFC69 unit test cases
dkoehler-dev Feb 15, 2024
3b07294
test(serialize): extend CFC69 serialize unit test cases
dkoehler-dev Feb 15, 2024
be7a3f1
refactor(send_custom_fc): add expect empty cursor statement to parse …
dkoehler-dev Feb 15, 2024
64d60b2
docs(send_custom_fc): extend documentation for the CFC69 implementation
dkoehler-dev Feb 16, 2024
6bc6548
test(integration cfc): improve integration test readability
dkoehler-dev Feb 16, 2024
0cdc137
refactor(custom_FC): implement dynamic CFC handling
dkoehler-dev Feb 21, 2024
d85e629
refactor(custom_client): remove unused print_read_result() function
dkoehler-dev Feb 22, 2024
9e1fb98
feat(function): make CFC FC const more readable
dkoehler-dev Feb 22, 2024
988ef20
refactor(integration_test): add CFC69 integration test
dkoehler-dev Feb 22, 2024
4f994dd
refactor(send_custom_fc): improve vector handling
dkoehler-dev Feb 22, 2024
280607a
refactor(parse): improve CFC vector handling
dkoehler-dev Feb 22, 2024
7d4c3b9
fix(serialize): adjust CFC serialization to new CFC struct
dkoehler-dev Feb 22, 2024
5f5ca0b
feat(handler): add cfc handler traits
dkoehler-dev Feb 22, 2024
f778102
refactor(task): adjust CFC request logic to use the correct handler f…
dkoehler-dev Feb 22, 2024
5e67614
refactor(request): adjust CFC request logic to use the correct const …
dkoehler-dev Feb 22, 2024
dcfdfe6
refactor(custom_client): add cfc69 requesthandler to test the CFC69 r…
dkoehler-dev Feb 22, 2024
9c7abd0
refactor(task): adjust CFC cient request logic to use the correct con…
dkoehler-dev Feb 22, 2024
c0261cd
fix(custom_fc): fix CFC69 response always the same
dkoehler-dev Feb 23, 2024
5262d87
refactor(custom_fc): implement copy trait for CFC
dkoehler-dev Feb 23, 2024
0048131
fix(custom_fc): add lifetime specifier to CFC
dkoehler-dev Feb 23, 2024
7318370
fix(send_custom_fc): remove the introduced lifetime specifier from th…
dkoehler-dev Mar 4, 2024
9912be2
fix(send_custom_fc): fix the unmodified CFC return object
dkoehler-dev Mar 4, 2024
8700378
refactor(send_custom_fc): fix unmodified CFC object, introduce byte_c…
dkoehler-dev Mar 4, 2024
554b6c7
fix(send_custom_fc): fix incorect CFC buffer population
dkoehler-dev Mar 5, 2024
62e7bcc
test(send_custom_fc): adjust the CFC parse/serialize and integration …
dkoehler-dev Mar 5, 2024
6c11ee2
test(send_custom_fc): add CFC integration tests
dkoehler-dev Mar 5, 2024
5a52c1d
refactor(send_custom_fc): improved CFC output
dkoehler-dev Mar 6, 2024
9e81fdb
test(custom_server): Implement test CFC handlers to simulate differen…
dkoehler-dev Mar 6, 2024
2ba9ce0
feat(send_custom_fc): improve CFC documentation
dkoehler-dev Mar 6, 2024
52cd82a
test(send_custom_fc): remove failing incorrect CFC69 serial test
dkoehler-dev Mar 6, 2024
d89f403
refactor(send_custom_fc): remove panic statement from CFC function code
dkoehler-dev Mar 11, 2024
563fc22
refactor(send_custom_fc): replace individual CFC handlers with a gene…
dkoehler-dev Mar 11, 2024
8b026af
refactor(send_custom_fc): implement general purpose CFC handler in cu…
dkoehler-dev Mar 11, 2024
790eb3b
fix(send_custom_fc): fix request/parsing error when handling the CFC …
dkoehler-dev Mar 12, 2024
afbfbca
refactor(send_custom_fc): remove the blocking of standard function co…
dkoehler-dev Mar 12, 2024
2d1ccab
feat(custom_server): add standard FC handling in the server implement…
dkoehler-dev Mar 12, 2024
14e6134
feat(send_custom_fc): add standard FC routing for a CFC request in me…
dkoehler-dev Mar 12, 2024
d91ec3f
refactor(send_custom_fc): remove panic statement when no valid CFC is…
dkoehler-dev Mar 12, 2024
0ef05e4
Revert "refactor(send_custom_fc): remove panic statement when no vali…
dkoehler-dev Mar 13, 2024
cbf2d2e
Revert "feat(send_custom_fc): add standard FC routing for a CFC reque…
dkoehler-dev Mar 13, 2024
19d99d7
Revert "feat(custom_server): add standard FC handling in the server i…
dkoehler-dev Mar 13, 2024
6957bae
Revert "refactor(send_custom_fc): remove the blocking of standard fun…
dkoehler-dev Mar 13, 2024
aca481f
feat(send_custom_fc): updated CFC logic because of reversal conflicts
dkoehler-dev Mar 13, 2024
5b4694c
test(send_custom_fc): adjust integration test for CFCs
dkoehler-dev Mar 13, 2024
e6d374d
refactor(send_custom_fc): correct invalid CFC server handler fc match…
dkoehler-dev Mar 13, 2024
c2cb3f0
test(send_custom_fc): add integration test for the CFC range from 0 t…
dkoehler-dev Mar 13, 2024
747712f
Merge pull request #6 from dkoehler-dev/feature-cfc-improvements
grewek Apr 3, 2024
b0e2245
refactor(CustomFunctionCode): add is_empty() func as suggested by clippy
Apr 3, 2024
ba08389
refactor(check_auth): collapsed cfc codes into ranges
Apr 3, 2024
2f85e28
chore(fmt): changed formatting by running cargo fmt
Apr 3, 2024
f7a051e
Merge branch 'feature/last-improvements-cfc' into feature/custom-fc
Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions rodbus/SCFC_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Send Custom Function Code (65-72 & 100-110)

This document provides a detailed overview of the implemented rodbus custom function code feature. These user-defined function codes fall within the range of 65 to 72 and 100 to 110, as specified in the MODBUS Application Protocol Specification V1.1b3 (Page 10, Section 5: Function Code Categories), and allow custom server-side execution logic.


## Introduction
The custom function codes enable the implementation of user-defined logic on a remote server device. It facilitates the transmission, reception, and processing of a custom function code with a variable-size data buffer.


## Request Structure
| Parameter | Size | Range / Value |
|---------------------|---------------|-----------------------|
| Function code | 1 Byte | 0x41-0x48 / 0x64-0x6E |
| Byte Count (Input) | 1 Byte | 0x00 to 0xFF (N*) |
| Byte Count (Output) | 1 Byte | 0x00 to 0xFF |
| Data | N* x 2 Bytes | 0x0000 to 0xFFFF |


## Response Structure
| Parameter | Size | Value/Description |
|---------------------|--------------|-----------------------|
| Function code | 1 Byte | 0x41-0x48 / 0x64-0x6E |
| Byte Count (Input) | 1 Byte | 0x00 to 0xFF (N*) |
| Byte Count (Output) | 1 Byte | 0x00 to 0xFF |
| Data | N* x 2 Bytes | 0x0000 to 0xFFFF |


## Error Handling
| Parameter | Size | Description |
|----------------|---------|----------------------|
| Function code | 1 Byte | Function code + 0x80 |
| Exception code | 1 Byte | 01 or 02 or 03 or 04 |

### Error Codes:
- **01**: Illegal Function
- **02**: Illegal Data Address
- **03**: Illegal Data Value
- **04**: Server Device Failure


## Usage Example
### Request to send the custom FC 69 with a buffer of [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE] (Byte Count = 4 -> 8 bytes):
| Request Field | Hex | Response Field | Hex |
|---------------------------|-----|------------------------|-----|
| Function code | 45 | Function code | 45 |
| Byte Count (Input) | 04 | Byte Count (Input) | 04 |
| Byte Count (Output) | 04 | Byte Count (Output) | 04 |
| Arg1 Hi | C0 | Arg1 Hi | C0 |
| Arg1 Lo | DE | Arg1 Lo | DF |
| Arg2 Hi | CA | Arg2 Hi | CA |
| Arg2 Lo | FE | Arg2 Lo | FF |
| Arg3 Hi | C0 | Arg3 Hi | C0 |
| Arg3 Lo | DE | Arg3 Lo | DF |
| Arg4 Hi | CA | Arg4 Hi | CA |
| Arg4 Lo | FE | Arg4 Lo | FF |


## Usage
Make sure that you are in the `rodbus` project directory.


### Start the custom_server example
- `cargo run --example custom_server -- tcp`
- Once it's up, run `ed` to enable decoding

Leave the terminal open and open another terminal.


### Start the custom_client example
- `cargo run --example custom_client -- tcp`
- Once it's up, run `ed` to enable decoding


### Send the Custom Function Code 69
In the terminal with the running custom_client example, run:
- `scfc <u8 Function Code> <u8 Byte Count Input> <u8 Byte Count Output> <u16 Arguments>`
- E.g. `scfc 0x45 0x02 0x02 0xC0DE 0xCAFE`
- The response would be for example: `fc: 0x45, bytes in: 2, bytes out: 2, values: [49375, 51967], hex: [0xC0DF, 0xCAFF]`


## Troubleshooting Tips
- Ensure the server and client are using the same communication method and are connected to each other.
- Check for any error codes in the response and refer to the error handling section for resolution.


## Additional Resources
- For more information on the MODBUS protocol and function codes, refer to the [MODBUS Application Protocol Specification V1.1b3](https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf).
20 changes: 18 additions & 2 deletions rodbus/examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ where
async fn run_tcp() -> Result<(), Box<dyn std::error::Error>> {
// ANCHOR: create_tcp_channel
let channel = spawn_tcp_client_task(
HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 502),
HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 10502),
1,
default_retry_strategy(),
DecodeLevel::default(),
Expand Down Expand Up @@ -96,7 +96,7 @@ async fn run_rtu() -> Result<(), Box<dyn std::error::Error>> {
async fn run_tls(tls_config: TlsClientConfig) -> Result<(), Box<dyn std::error::Error>> {
// ANCHOR: create_tls_channel
let channel = spawn_tls_client_task(
HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 802),
HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 10802),
1,
default_retry_strategy(),
tls_config,
Expand Down Expand Up @@ -267,6 +267,22 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box<dyn std::error::Err
print_write_result(result);
// ANCHOR_END: write_multiple_registers
}
"scfc" => {
// ANCHOR: send_custom_function_code
let fc = 0x45 as u8;
let byte_count_in = 0x04 as u8;
let byte_count_out = 0x04 as u8;
let values = vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE

let result = channel
.send_custom_function_code(
params,
CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values),
)
.await;
print_write_result(result);
// ANCHOR_END: send_custom_function_code
}
_ => println!("unknown command"),
}
}
Expand Down
235 changes: 235 additions & 0 deletions rodbus/examples/custom_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
use std::error::Error;
use std::net::{IpAddr, Ipv4Addr};
use std::process::exit;
use std::time::Duration;

use tokio_stream::StreamExt;
use tokio_util::codec::{FramedRead, LinesCodec};

use rodbus::client::*;
use rodbus::*;

// ANCHOR: runtime_init
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn Error>> {
// ANCHOR_END: runtime_init

// ANCHOR: logging
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.init();
// ANCHOR_END: logging

let args: Vec<String> = std::env::args().collect();
let transport: &str = match &args[..] {
[_, x] => x,
_ => {
eprintln!("please specify a transport:");
eprintln!("usage: outstation <transport> (tcp, rtu, tls-ca, tls-self-signed)");
exit(-1);
}
};
match transport {
"tcp" => run_tcp().await,
#[cfg(feature = "serial")]
"rtu" => run_rtu().await,
#[cfg(feature = "tls")]
"tls-ca" => run_tls(get_ca_chain_config()?).await,
#[cfg(feature = "tls")]
"tls-self-signed" => run_tls(get_self_signed_config()?).await,
_ => {
eprintln!(
"unknown transport '{transport}', options are (tcp, rtu, tls-ca, tls-self-signed)"
);
exit(-1);
}
}
}

struct LoggingListener;
impl<T> Listener<T> for LoggingListener
where
T: std::fmt::Debug,
{
fn update(&mut self, value: T) -> MaybeAsync<()> {
tracing::info!("Channel Listener: {:?}", value);
MaybeAsync::ready(())
}
}

async fn run_tcp() -> Result<(), Box<dyn std::error::Error>> {
// ANCHOR: create_tcp_channel
let channel = spawn_tcp_client_task(
HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11502),
1,
default_retry_strategy(),
DecodeLevel::default(),
Some(Box::new(LoggingListener)),
);
// ANCHOR_END: create_tcp_channel

run_channel(channel).await
}

#[cfg(feature = "serial")]
async fn run_rtu() -> Result<(), Box<dyn std::error::Error>> {
// ANCHOR: create_rtu_channel
let channel = spawn_rtu_client_task(
"/dev/ttySIM0", // path
rodbus::SerialSettings::default(), // serial settings
1, // max queued requests
default_retry_strategy(), // retry delays
DecodeLevel::new(
AppDecodeLevel::DataValues,
FrameDecodeLevel::Payload,
PhysDecodeLevel::Nothing,
),
Some(Box::new(LoggingListener)),
);
// ANCHOR_END: create_rtu_channel

run_channel(channel).await
}

#[cfg(feature = "tls")]
async fn run_tls(tls_config: TlsClientConfig) -> Result<(), Box<dyn std::error::Error>> {
// ANCHOR: create_tls_channel
let channel = spawn_tls_client_task(
HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11802),
1,
default_retry_strategy(),
tls_config,
DecodeLevel::new(
AppDecodeLevel::DataValues,
FrameDecodeLevel::Nothing,
PhysDecodeLevel::Nothing,
),
Some(Box::new(LoggingListener)),
);
// ANCHOR_END: create_tls_channel

run_channel(channel).await
}

#[cfg(feature = "tls")]
fn get_self_signed_config() -> Result<TlsClientConfig, Box<dyn std::error::Error>> {
use std::path::Path;
// ANCHOR: tls_self_signed_config
let tls_config = TlsClientConfig::self_signed(
Path::new("./certs/self_signed/entity2_cert.pem"),
Path::new("./certs/self_signed/entity1_cert.pem"),
Path::new("./certs/self_signed/entity1_key.pem"),
None, // no password
MinTlsVersion::V1_2,
)?;
// ANCHOR_END: tls_self_signed_config

Ok(tls_config)
}

#[cfg(feature = "tls")]
fn get_ca_chain_config() -> Result<TlsClientConfig, Box<dyn std::error::Error>> {
use std::path::Path;
// ANCHOR: tls_ca_chain_config
let tls_config = TlsClientConfig::full_pki(
Some("test.com".to_string()),
Path::new("./certs/ca_chain/ca_cert.pem"),
Path::new("./certs/ca_chain/client_cert.pem"),
Path::new("./certs/ca_chain/client_key.pem"),
None, // no password
MinTlsVersion::V1_2,
)?;
// ANCHOR_END: tls_ca_chain_config

Ok(tls_config)
}

/*fn print_read_result<T>(result: Result<Vec<Indexed<T>>, RequestError>)
where
T: std::fmt::Display,
{
match result {
Ok(registers) => {
for register in registers {
println!("index: {} value: {}", register.index, register.value);
}
}
Err(rodbus::RequestError::Exception(exception)) => {
println!("Modbus exception: {exception}");
}
Err(err) => println!("read error: {err}"),
}
}*/

fn print_write_result<T>(result: Result<T, RequestError>) {
match result {
Ok(_) => {
println!("write successful");
}
Err(rodbus::RequestError::Exception(exception)) => {
println!("Modbus exception: {exception}");
}
Err(err) => println!("writer error: {err}"),
}
}

async fn run_channel(mut channel: Channel) -> Result<(), Box<dyn std::error::Error>> {
channel.enable().await?;

// ANCHOR: request_param
let params = RequestParam::new(UnitId::new(1), Duration::from_secs(1));
// ANCHOR_END: request_param

let mut reader = FramedRead::new(tokio::io::stdin(), LinesCodec::new());
while let Some(line) = reader.next().await {
let line = line?; // This handles the Some(Err(e)) case by returning Err(e)
let parts = line.split_whitespace().collect::<Vec<&str>>();
match parts.as_slice() {
["x"] => return Ok(()),
["ec"] => {
channel.enable().await?;
}
["dc"] => {
channel.disable().await?;
}
["ed"] => {
channel
.set_decode_level(DecodeLevel::new(
AppDecodeLevel::DataValues,
FrameDecodeLevel::Payload,
PhysDecodeLevel::Data,
))
.await?;
}
["dd"] => {
channel.set_decode_level(DecodeLevel::nothing()).await?;
}
["scfc", fc_str, bytes_in_str, bytes_out_str, values @ ..] => {
let fc = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap();
let byte_count_in =
u8::from_str_radix(bytes_in_str.trim_start_matches("0x"), 16).unwrap();
let byte_count_out =
u8::from_str_radix(bytes_out_str.trim_start_matches("0x"), 16).unwrap();
let values: Vec<u16> = values
.iter()
.filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok())
.collect();

if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) {
let result = channel
.send_custom_function_code(
params,
CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values),
)
.await;
print_write_result(result);
} else {
println!("Error: CFC number is not inside the range of 65-72 or 100-110.");
}
}
_ => println!("unknown command"),
}
}
Ok(())
}
Loading