How to implement a message with a custom function code in pymodbus? - 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?

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()

Unit parameter in pymodbus custom request

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)

How to access the raw content from a response in Vapor 3 unit test?

I'm coming from using tooling such as SuperTest with NodeJS and looking for relevant equivalents to support testing with Vapor 3 and server side swift.
I see a pattern of using making a testable application with Vapor 3 to do testing of endpoints, examples being https://github.com/raywenderlich/vapor-til/blob/master/Tests/AppTests/Application%2BTestable.swift and the write-up at https://medium.com/swift2go/vapor-3-series-iii-testing-b192be079c9e.
When using these in tests, the format generally looks something like:
func testGettingASingleUserFromTheAPI() throws {
let user = try User.create(name: usersName, username: usersUsername, on: conn)
let receivedUser = try app.getResponse(to: "\(usersURI)\(user.id!)", decodeTo: User.Public.self)
XCTAssertEqual(receivedUser.name, usersName)
XCTAssertEqual(receivedUser.username, usersUsername)
XCTAssertEqual(receivedUser.id, user.id)
}
(from Vapor-TIL example code)
In all of these examples, the return values are really set to be handed back to something decodable (the decodeTo: kind of setup). In some cases in my Vapor 3 code, I want to just validate some non-JSON encoded results - just simple strings, and validate the results - but I've not found the methods to get into the content or convenient ways to validate it with XCTAssert.
response.content is available, a container around the overall response (of type ContentContainer). Are there some examples or good ways at getting to the underlying content representation to validate them directly?
You could write your own additional methods in Application+Testable like
func getRawResponse(to path: String) throws -> Response {
return try self.sendRequest(to: path, method: .GET)
}
func getStringResponse(to path: String) throws -> String {
let response = try self.getRawResponse(to: path)
guard let data = response.http.body.data,
let string = String(data: data, encoding: .utf8) else {
throw SomeError("Unable to decode response data into String")
}
return string
}
and then call them to get either raw Response or decoded String like
func testGettingHelloWorldStringFromTheAPI() throws {
let string = try app. getStringResponse(to: "some/endpoint")
XCTAssertEqual(string, "Hello world")
}

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.