POSTing Modbus data on HTTP endpoint

I am working on a task assigned by my company. In this task, I have to send the data received by the Modbus client on the HTTP endpoint. I am sharing the code below

// Modbus TCP Client
use std::error::Error;

use futures::SinkExt;
use tokio::net::TcpStream;
use tokio_stream::StreamExt;
use tokio_util::codec::Framed;
use easy_modbus::{Frame, codec::TcpClientCodec};
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let addr = "127.0.0.1:502".to_string();
    let stream = TcpStream::connect(&addr).await?;
    let mut transport = Framed::new(stream, TcpClientCodec);
    let frame = Frame::tcp();
    let request = frame.read_coils_request(0x01, 0x02, 0x08);

transport.send(request).await?;
    while let Some(response) = transport.next().await {
        match response {
            Ok(response) => {
                println!("{:?}", response);

               
            }
            Err(e) => {
                return Err(e.into());
            }
        }
    }
    Ok(())
}

TASK: read the register values of some TCP Modbus servers reachable from the local network send the data to an HTTP server reachable from within the Wireguard network To do all this the client is going to first call an HTTP endpoint that's going to return the configuration as JSON. The data contained in this JSON is going to be: the Modbus server addresses the registers it needs to read from each server including the type of data the register holds (boolean, integer, etc.) how frequently to read the data from each register a single HTTP endpoint to POST all of the data to Each time the data is collected the client is then going to make an HTTP POST to the second above-mentioned endpoint with a JSON of the data it collected. If any error is encountered while trying to call this second endpoint, the data will need to be persisted to disk so that the request can be subsequently retried. If the client can't reach the Modbus server, or there's anything preventing the data from being collected, a message should be sent to the second endpoint to report it.

As the code shows, I am done with getting Modbus data (JSON) but am now stuck at the point of sending it to an HTTP endpoint.

Any help will be appreciated

Thank you

You can simply use a library like reqwest (which btw has tokio support) to do the HTTP calls. In general, you don't ever want to be doing HTTP calls using TCP sockets directly since it's too complex, and not useful unless you are trying to achieve something else.

1 Like

I used reqwest but I am stuck in the coding. can you please code it? that will be a great help and much appreciated.

Stuck where? Please be more specific.

1 Like

I received this JSON data from the server.

ReadCoils(Head { tid: 1, pid: 0, length: 5, uid: 1, function: ReadCoils, version: Tcp, is_exception: false }, 
ReadCoilsResponse { bytes_number: 2, values: [0, 1] })

Now I want to send this data on the HTTP endpoint.
I want to program it but I am stuck.

You probably want something like:

let client = reqwest::Client::new();
let res = client.post(&url)
    .json(&data)
    .send()
    .await?;

Reuse the client between calls. Also to send JSON data, make sure to enable the json feature for reqwest in your Cargo.toml.

Thanks for the code. i used it in the program but got the following error.
error

See the serde_json documentation for how to either manually convert your values to serde_json::Value or derive serde::Serialize

I used serde_json but got this error. I tried a lot but couldn't sort it out why it is throwing this error.

You imported serde_json's type alias called Result that only has one generic type parameter (the error parameter is filled in with serde_json's Error type) but you're trying to pass two like the std::result::Result accepts.

Replace any usages of Result with a single type parameter with serde_json::Result and remove the use for serde_json::Result

either i remove or use serde_json::Result
I got the same error.
can you please guide me by looking at the following code?

#![allow(unused_imports)]
use std::error::Error;

use futures::SinkExt;
use tokio::net::TcpStream;
use tokio_stream::StreamExt;
use tokio_util::codec::Framed;
use easy_modbus::{Frame, codec::TcpClientCodec};
use serde::{Deserialize, Serialize};
use serde_json::Result;

//use std::collections::HashMap;

// use derive_builder::Builder;
// use rustify::{Client, Endpoint};
// use rustify_derive::Endpoint;

    // #[derive(Builder, Endpoint)]
    // #[endpoint(path = "127.0.0.1:502", method = "POST", builder = "true")]
    // #[builder(setter(into))] // Improves the building process
    // struct Test {
      
    // }


#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let addr = "127.0.0.1:502".to_string();
    let stream = TcpStream::connect(&addr).await?;
    let mut transport = Framed::new(stream, TcpClientCodec);
    let frame = Frame::tcp();
    let request = frame.read_coils_request(0x01, 0x02, 0x08);
    
    // let endpoint = request.build().unwrap();
    // let client = Client::default("127.0.0.1:502");
    // let result = endpoint.exec(&client).await; // Sends POST request to http://127.0.0.1:502
    // assert!(result.is_ok());

    let client = reqwest::Client::new();
    let res = client.post(&addr)
    .json(&request)
    .send()
    .await?;
    serde_json::to_string(&res)?;
    println!("{}", res.text().await?);

    // let endpoint = warp::post()
    //     .and(warp::body::json())
    //     .map(|body| warp::reply::json(&request));
    // warp::serve(endpoint).run(([127, 0, 0, 1], 5000)).await;

    transport.send(request).await?;
    while let Some(response) = transport.next().await {
        match response {
            Ok(response) => {
                println!("{:?}", response);

               
            }
            Err(e) => {
                return Err(e.into());
            }
        }
    }
    Ok(())
}