Unit parameter in pymodbus custom request - pymodbus

this is my firt post here.
I was lurking for a while.
So, I'm having problem regarding custom message in pymodbus ModbusTcpClient
I'm playing with one old Modbus device which has custom registers and commands.
I am able to read/write coils, registers, etc.
Problem is that this device needs special command for some sort of reset.
I made some wireshark sniffing and I made custom message, but I'm stuck in defining unit parameter.
here is code snippet:
class CustomModbusRequest(ModbusRequest):
function_code = 8
def __init__(self, address):
ModbusRequest.__init__(self)
self.address = address
self.count = 1
def encode(self):
return struct.pack('>HH', self.address, self.count)
def decode(self, data):
self.address, self.count = struct.unpack('>HH', data)
def execute(self, context):
if not (1 <= self.count <= 0x7d0):
return self.doException(ModbusExceptions.IllegalValue)
if not context.validate(self.function_code, self.address, self.count):
return self.doException(ModbusExceptions.IllegalAddress)
values = context.getValues(self.function_code, self.address,
self.count)
return CustomModbusResponse(values)
def custom_8():
client = ModbusTcpClient('192.168.0.222')
connection = client.connect()
request = CustomModbusRequest(170)
result = client.execute(request)
print(result)
time.sleep(1)
In normal request for read register there is specified unit parameter, like this:
request = client.read_input_registers(513,4, unit=0x4)
In custom request, I don't know how to specify this. In wireshark I can see that in custom message, I'm sending request to address 0, and I need to use address 4.
Please, help.

You will have to pass the unit to custom message and that should do the trick.
request = CustomModbusRequest(170, unit=<unit_id>). You also will have to update the __init__ of CustomModbusRequest to pass additional kwargs to parent.
class CustomModbusRequest(ModbusRequest):
function_code = 8
def __init__(self, address, **kwargs):
ModbusRequest.__init__(self, **kwargs)
self.address = address
self.count = 1
...
...
request = CustomModbusRequest(170, unit=<unit_id>)
result = client.execute(request)

Related

Is there a way to dynamically pass payload to my tests in pytest?

I want to the tests to get request body data dynamically
class TestPlaylistCreateView:
def test_create_playlist_with_valid_name(self, authorized_api_client):
url = reverse("create-playlist")
data = {
"name": name,
"is_public": "True"
} # <------- concrete payload
response = authorized_api_client.post(url, data)
assert response.status_code == status.HTTP_201_CREATED
def test_create_already_created_playlist(self, authorized_api_client, create_playlist):
url = reverse("create-playlist")
data = {
"name": create_playlist.name,
"is_public": create_playlist.is_public
} # <------- again the same payload
response = authorized_api_client.post(url, data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
I would like to avoid repeating myself everytime I need request body data. is there a way to do it, maybe through a fixture or something.
my fixtures for the test
#pytest.fixture
def create_user():
return UserFactory()
#pytest.fixture
def authorized_api_client(create_user):
api_client = APIClient()
api_client.force_authenticate(user=create_user)
return api_client
#pytest.fixture
def create_playlist():
return PlaylistFactory()

How to implement a message with a custom function code in pymodbus?

I'm currently trying to implement a modbus request that has a custom function code. The implementation is based on this example: custom_message.py
import struct
from pymodbus.pdu import ModbusRequest, ModbusResponse
from pymodbus.client.sync import ModbusTcpClient
client = ModbusTcpClient('192.168.0.55')
connection = client.connect()
class CustomModbusResponse(ModbusResponse):
# some fancy decoding should be done here..
pass
class CustomModbusRequest(ModbusRequest):
function_code = 55
def __init__(self, address):
ModbusRequest.__init__(self)
self.address = address
self.count = 1
def encode(self):
return struct.pack('>HH', self.address, self.count)
def decode(self, data):
self.address, self.count = struct.unpack('>HH', data)
def execute(self, context):
if not (1 <= self.count <= 0x7d0):
return self.doException(ModbusExceptions.IllegalValue)
if not context.validate(self.function_code, self.address, self.count):
return self.doException(ModbusExceptions.IllegalAddress)
values = context.getValues(self.function_code, self.address,
self.count)
return CustomModbusResponse(values)
request = CustomModbusRequest(0)
result = client.execute(request)
print(result)
The request works as expected. I can see the correct response on the network layer. However I can't parse the result. Pymodbus is throwing the following error:
DEBUG:pymodbus.factory:Factory Response[55]
ERROR:pymodbus.factory:Unable to decode response Modbus Error: Unknown response 55
ERROR:pymodbus.transaction:Modbus Error: [Input/Output] Unable to decode request
The example states that in this case, I would have to:
If you implement a new method that is not currently implemented, you
must register the request and response with a ClientDecoder factory.
Is there an elegant way to do this without patching the library?

How to use return of one gatling request into another request - Scala

In the following code, I am getting a token in the first Gatling request, saving it in a variable named auth. However, when I try to use it in the second request, it is sending empty string in place of auth variable. So for some reason, the auth string is not being updated till the time it is being used in the second request. Can anyone suggest any workaround so that I can use the value returned in one request into another request?
Code:
val headers_10 = Map("Content-Type" -> "application/x-www-form-urlencoded")
var a= "qwerty91#gmail.com"
var auth = ""
val scn = scenario("Scenario Name") // A scenario is a chain of requests and pauses
.exec(http("request_1") // Here's an example of a POST request
.post("/token")
.headers(headers_10)
.formParam("email", a)
.formParam("password", "password")
.transformResponse { case response if response.isReceived =>
new ResponseWrapper(response) {
val a = response.body.string
auth = "Basic " + Base64.getEncoder.encodeToString((a.substring(10,a.length - 2) + ":" + "junk").getBytes(StandardCharsets.UTF_8))
}
})
.pause(2)
.exec(http("request_2")
.get("/user")
.header("Authorization",auth)
.transformResponse { case response if response.isReceived =>
new ResponseWrapper(response) {
val a = response.body.string
}
})
You should store the value you need in the session. Something like this will work, although you'll have to tweak the regex and maybe some other details:
val headers_10 = Map("Content-Type" -> "application/x-www-form-urlencoded")
var a= "qwerty91#gmail.com"
var auth = ""
val scn = scenario("Scenario Name") // A scenario is a chain of requests and pauses
.exec(http("request_1") // Here's an example of a POST request
.post("/token")
.headers(headers_10)
.formParam("email", a)
.formParam("password", "password")
.check(regex("token: (\\d+)").find.saveAs("auth")))
.pause(2)
.exec(http("request_2")
.get("/user")
.header("Authorization", "${auth}"))
Here's the documentation on "checks", which you can use to capture values from a response:
http://gatling.io/docs/2.2.2/http/http_check.html
Here is the documentation on the gatling EL, which is the easiest way to use session variables (this is the "${auth}" syntax in the last line above):
http://gatling.io/docs/2.2.2/session/expression_el.html

Return from function in body and not only in last line in REST API Scala and play

I develop Rest API with scala and play framework.
In my product controller, I do validation of parameters received.
In case they fail from some reason, I would like to response with BadRequest in the middle of the function and not in the last line as scala works..
In the code below - Code continues running to the Ok line.. which is wrong, I want to return !
def getProduct(lang: String, t: String, ids: String) = Action {
val productIdsList = ids.split(",").toList
if (productIdsList.length.equals(1) && productIdsList(0).equals("")) //Validate input params are product Ids and not empty !
{
var errorResponse:ErrorResponse[String] = ErrorResponse(ErrorCode.GeneralError, "No products IDs", 500)
BadRequest(Json.toJson(errorResponse))//maybe return BadRequest(Json.toJson(errorResponse) ??
}
val results = productService.getProducts(GetProductsRequest(lang,t,productIdsList));
Ok(Json.toJson(results))
// TODO: handle error
}
If implemented as:
return BadRequest(...)
It reply with error:
"method getProduct has return statement; needs result type"
I Understand this is bad practice, so what is the best practice for quitting the function without finishing it (and not throwing exceptions..)
Just put an else branch, so there's nowhere to continue:
def getProduct(lang: String, t: String, ids: String) = Action {
val productIdsList = ids.split(",").toList
if (productIdsList.length.equals(1) && productIdsList(0).equals("")){ //Validate input params are product Ids and not empty !
var errorResponse:ErrorResponse[String] = ErrorResponse(ErrorCode.GeneralError, "No products IDs", 500)
BadRequest(Json.toJson(errorResponse))//maybe return BadRequest(Json.toJson(errorResponse) ??
}else{
val results = productService.getProducts(GetProductsRequest(lang,t,productIdsList));
Ok(Json.toJson(results))
// TODO: handle error
}
}

Databinder dispatch: Get uncompressed content of a 403 response

I am using databinder dispatch for making HTTP requests which works nicely, as long as the web server returns a 404.
If the request fails, the web server returns a 403 status code and provides a detailed error message in the response body as XML.
How to read the xml body (regardless of the 403), e.g. how can I make dispatch ignore all 403 errors?
My code looks like this:
class HttpApiService(val apiAccount:ApiAccount) extends ApiService {
val http = new Http
override def baseUrl() = "http://ws.audioscrobbler.com/2.0"
def service(call:Call) : Response = {
val http = new Http
var req = url(baseUrl())
var params = call.getParameterMap(apiAccount)
var response: NodeSeq = Text("")
var request: Request = constructRequest(call, req, params)
// Here a StatusCode exception is thrown.
// Cannot use StatusCode case matching because of GZIP compression
http(request <> {response = _})
//returns the parsed xml response as NodeSeq
Response(response)
}
private def constructRequest(call: Call, req: Request, params: Map[String, String]): Request = {
val request: Request = call match {
case authCall: AuthenticatedCall =>
if (authCall.isWriteRequest) req <<< params else req <<? params
case _ => req <<? params
}
//Enable gzip compression
request.gzip
}
}
I believe something like this works:
val response: Either[String, xml.Elem] =
try {
Right(http(request <> { r => r }))
} catch {
case dispatch.StatusCode(403, contents) =>
Left(contents)
}
The error will be in Left. The success will be in Right. The error is a String that should contain the XML response you desire.
If you need more, I believe you can look at HttpExecutor.x, which should give you full control. It's been a while since I've used dispatch, though.
Also, I'd suggest using more val's and less var's.