Act on response from usb to serial device command.

Hi

I’m looking for some help with the following code.

It seems to run successfully (the matrix’s Input changes and a response is captured) but it doesn’t seem to progress (and print out) the required analysis results/response based on what the the HDMI matrix sent back.

[code]function HDMIA1()
local socket = require(“socket”)
host = “192.168.1.204”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
local sres, serr = c:send(string.char(0x02,0x32,0x31,0x31,0x03))
print(“Send:”, sres, serr)
local data, rerr = c:receive(175)
luup.log (data)
print (“Receive:”, data, rerr)
c:close()

– if successful the hdmi switch will return a message which includes the words “Status code:100031111110001” - so I’ve tried to use a lua expression to extract that if it’s there.

local result = data:match “%a+%s%a+:%d+”
print (“” …result)

if (result == “Status code:100031111110001”)
then
luup.log(“it worked”)
print(“it worked”)
if (result ~= “Status code:100031111110001”)
then
luup.log(“it failed”)
print(“it failed”)
else print(“neither were seen it has just failed”)
end
end
end

HDMIA1()[/code]

You’re expecting exactly 175 bytes to be returned? Unwise.

Also, the timeout() call has a problem in some implementations.

Hi @akbooer

I had gradually increased the byte size so I could see more of what’s returned - so I can easily trim that a bit, but as the connection uses Ser2net it seems I will still need quite a bit to ensure I capture the required status code.

This is what Luatest returns.

[u][b]LuaTest[/b][/u]

LuaTest 1.5.2

Lua file: /nas/luatest/octavia.lua

Results
No errors
Runtime: 866.0 ms
Code returned: nil

Print output
Send: 5
Receive: ???
ser2net port 4002 device /dev/ttyUSB0 [9600 N81] (Debian GNU/Linux)

S211E

Status code:100011111110001

**Refer to Install Guide or FAQ (www.octavainc.com)f

Status code:100011111110001

Does the use of a timeout impact the running of all the code? I assumed the timeout who just related to the socket (USB/serial) connection ?

Your if/else check doesn’t look like it is what you want. If I add some indentation (no code change), you can see it won’t get to the inner statements unless the first if statement is true:if (result == "Status code:100031111110001") then luup.log("it worked") print("it worked") if (result ~= "Status code:100031111110001") then luup.log("it failed") print("it failed") else print("neither were seen it has just failed") end end
You probably want something like this:if (result == "Status code:100031111110001") then luup.log("it worked") print("it worked") else luup.log("it failed") print("it failed") end
But that’s only relevant if you can get to that point; I’m not an expert on the receive data portion of your code.

Hi @Jswim788

Thanks for that,

As the message being returned acts as either confirmation of a success transaction or a failure of one - I felt i needed to be more specific in my checking.

Unless I’m mistaken, wouldn’t having just the one ‘if’ mean that the “else” will by default work as a catch all else - and not only be returned with a mismatch but also if there is any failure in the code itself etc?

I did not realise that things on,y progress if the first ‘if’ is met, therefore is this a situation where using ‘elseif’ is a better route ?

Yes, elseif is what you want. The “then” to the “end” are all qualified by that first “if”. So definitely you want an “elseif”.

The 2 checks you have looked identical to me. So it looked like this:

if result = “something” then
do stuff
end
if result ~= “something” then
do other stuff
end

which can be replaced with a simple “else”. But maybe you intended for the 2 string checks to be unique? (Or maybe they are and I got lost in the string!)

Ok, so as I would like to be more specific in my checking of the response/result is this what you are suggesting - to do it in sort of blocks ?

if result == "Status code:100031111110001" print ("Expected status code received - so success") elseif result ~= "Status code:100031111110001" print ("Unexpected status code received - so failure ") else print ("Something else happened, it likely failed ") end

HOLD ON - I’ve just realised I’m not making myself clear, the reason for the second “~=” check is to identify if another status code (which I do not know) is returned/received - but I can see now that the code I’ve done is no where near as intelligent as it needs to be to work that out:

How would I do a wild card type check of the numbers returned after the “Status Code:” text to report they are different - The status code I’ve specifically listed looks to the one provided when the command was received successful, but I want the code to be open to receiving others, even if I have not been able to see any yet (BTW - there is no manual for these codes) ?

Why don’t you simply read all of the response? Then you’re sure to capture what you need…

local data, rerr = c:receive "*a"

In fact, in Lua, multiple if-then-elseif statements are rarely appropriate. Lua has associative arrays, and that means that anything can be an array subscript.

I’d suggest that you just strip out the relevant status code return from the response string and use it as an array subscript. Lua does all the checking for you in optimised code…

local result = data: match "Status code:(%d+)" or "error"

message = {
  ["error"] = "Something else happened, it likely failed",
  ["100031111110001"] = "Expected status code received - so success",
  -- extend this with new codes when needed
}

print (message[result] or "Unexpected status code received - so failure")

It’s neat, easy to extend, and much more readable.

If you actually need to do different things, rather than just print a response, you put functions into the ‘message’ array, rather than messages, and call them…

local result = data: match "Status code:(%d+)" or "error"

local function success_action (...)
  -- whatever
end

local function unknown_action (...)
  -- whatever else
end

local function error_action (...)
  -- something to do when things go wrong
end

action = {
  ["error"] = error_action,
  ["100031111110001"] = success_action,
  -- extend this with new codes when needed
}

(action[result] or unknown_action) (any_parameters_you_like)

Now you’re starting to use some of the real power of the Lua language.

Thanks @akbooer

My head hurts :slight_smile: - I love this stuff , but really struggle to grasp all the nuances :slight_smile:

Ok, it seems I’m going to need to create a function for all the key steps - which I guess will be as follows

[ul][li][font=verdana]Send hex command to matrix and capture response[/font][/li]
[li][font=verdana]Intepret response from matrix and call appropriate function either success or failure [/font][/li]
[li][font=verdana]The command was successful function[/font][/li]
[li][font=verdana]The command failed function [/font][/li][/ul]
[font=verdana]As the only difference in the command code is the hex value used, it would make sense to just send the function request with the required hex value provided via an argument. Assuming I have got the terminology right :slight_smile: [/font]

  1. The Matrix Hex Command function

[font=verdana]This route currently fails as I think the commas needed in the argument part breaks it ? Is there a way around this so the various hex value can be provided in a way that they are passed into the function correctly ?[/font]

[code]function matrix_hex_command(hexcode)
local socket = require(“socket”)
host = “192.168.1.204”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
local sres, serr = c:send(string.char(hexcode))
local data, rerr = c:receive “*a”

local result = data: match"Status code:(%d+)"
c:close()

matrix_hex_response()
end
[/code]

This is how I’m thinking I can then send the matrix hex command…

matrix_hex_command(0x02,0x32,0x31,0x31,0x03)
  1. The Matrix Hex Interpretation Function

I’ve assumed this should be called as the final part of the matrix hex command function. However I’m not sure how the

[code]local function matrix_hex_interpretation()

– ok so this is an array and I can call functions from this based on the result ?

action = { [“100031111110001”] = command_successful(),
[“100031111110001”] = command_failed(),
– extend this with new codes when needed}
end[/code]

[size=9px][font=verdana]Question : how does the interpretation function see the response from the matrix to do it’s action. [/font][/size]

3) Command was Successful Function

[font=verdana]Action - this is to be worked on I would like to look at using luup.task to show the results in the Vera information window[/font]

Local function command_successful () . . . end[font=verdana]

4) Command sent failed Function
[/font][font=verdana]To be worked on - same reason as above.

[/font] Local function command_failed () . . . end[font=verdana]

Please let me know your thoughts ?[/font]

Ah, you caught my response before I edited it. If you re-read it, you may find it a bit clearer. My mistake was to think that you already had two different return codes to deal with but, looking at the array, I see they were the same code. I refactored slightly, but the intention and approach is the same.

Ok, it seems I'm going to need to create a function for all the key steps

That’s really good practice, anyway.

The Matrix Hex Command function

Depending on the device you’re communication with, you may not need to create a new TCP connection for every command? I don’t know, you’d need to refer to the documentation. Either way, I’d not have the require statement in the, since it only needs to be done once.

string.char() takes multiple arguments, doesn’t matter how many or what they’re called, so I’d write this as what’s called a variadic function:

function matrix_hex_command(...)
-- your previous code here
local sres, serr = c:send(string.char(...))
-- etc.
end
Question : how does the interpretation function see the response from the matrix to do it's action.

Just pass it the value in you call invocation:

(action[result] or unknown_action) (result)

I hope it makes it clearer. If not, ask again.

Hi @akbooer

OK - this is great stuff, not sure I follow everything, but with yours and others help - I’m going to try and work through this step by step. :slight_smile: So lets start with the initial Send Matrix Hex Command function

Objective - Is to send a HEX command to a HDMI Matrix Switch and capture the response that comes back, for another function to analyse/invoke actions.

The use of a ‘variadic function’ looks to have solved the matrix_hex_command function - thanks so much for that ! That allows me to use just the following code now to make changes.

matrix_hex_command(0x02,0x32,0x31,0x32,0x03) 

Regarding "local data, rerr = c:receive “*a” that does not return anything and always times out, even if i extend to [font=dejavu sans mono][size=x-small]c:settimeout ()[/size][/font] to 10 seconds.
Only specifying a number of bytes e.g “local data, rerr = c:receive(205)” seems to return information ** Howrver 208 bytes seems to be the max, any more and it times out too **

Sorry that’s my mistake - after further testing there are other “Status Codes” values that come back if the request sent was successful, and there could be more, hence the array idea to capture and log them seems a great idea idea.

Output A - Input 1 = Status code:100011111110101
Output A - Input 2 = Status code:100021111110101
Output A - Input 3 = Status code:100031111110101
Output A - Input 4 = Status code:100041111110101

Output B - Input 1 = Status code:100021211110101
Output B - Input 2 = Status code:100022211110101
Output B - Input 3 = Status code:100023211110101
Output B - Input 3 = Status code:100024211110101

Output C & D to do…

Power On = Status code:100024231110101
Power Off = Status code:100024231110000

Code

[code]function send_matrix_hex_command(…)
local socket = require(“socket”)
host = “192.168.1.204”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
local sres, serr = c:send(string.char(…))
– local data, rerr = c:receive “*a”
local data, rerr = c:receive(205)
print (“Receive:”, data, rerr)
c:close()

luup.call_delay(“matrix_reply_interpretation”, 1)
end[/code]

Then the required HEX commands/calls can be done using this format…

[code]

send_matrix_hex_command(0x02,0x32,0x31,0x31,0x03)[/code]

Does that look good ?

For reference here are all the HEX Code commands i can send.

[code]-- Output A
– send_matrix_hex_command(0x02,0x32,0x31,0x31,0x03)
– send_matrix_hex_command(0x02,0x32,0x31,0x32,0x03)
– send_matrix_hex_command(0x02,0x32,0x31,0x33,0x03)
– send_matrix_hex_command(0x02,0x32,0x31,0x34,0x03)

– Output B
– send_matrix_hex_command(0x02,0x32,0x32,0x31,0x03)
– send_matrix_hex_command(0x02,0x32,0x32,0x32,0x03)
– send_matrix_hex_command(0x02,0x32,0x32,0x33,0x03)
– send_matrix_hex_command(0x02,0x32,0x32,0x34,0x03)

– Output C
– send_matrix_hex_command(0x02,0x32,0x33,0x31,0x03)
– send_matrix_hex_command(0x02,0x32,0x33,0x32,0x03)
– send_matrix_hex_command(0x02,0x32,0x33,0x33,0x03)
– send_matrix_hex_command(0x02,0x32,0x33,0x34,0x03)

– Output D
– send_matrix_hex_command(0x02,0x32,0x34,0x31,0x03)
– send_matrix_hex_command(0x02,0x32,0x34,0x32,0x03)
– send_matrix_hex_command(0x02,0x32,0x34,0x33,0x03)
– send_matrix_hex_command(0x02,0x32,0x34,0x34,0x03)

– Port Status
– send_matrix_hex_command(0x02,0x30,0x30,0x31,0x03)

– LED On
– send_matrix_hex_command(0x02,0x30,0x30,0x33,0x03)

– LED Off
– send_matrix_hex_command(0x02,0x30,0x30,0x34,0x03)

– Power On
– send_matrix_hex_command(0x02,0x30,0x30,0x35,0x03)

– Power Off
– send_matrix_hex_command(0x02,0x30,0x30,0x36,0x03)
[/code]

Once again, I’d recommend that you move the require statement to outside the function.

The assert statement will raise an error if the connection fails, this is probably not what you want.

I don’t get what the call_delay is all about.

[quote=“akbooer, post:13, topic:197127”]Once again, I’d recommend that you move the require statement to outside the function.

The assert statement will raise an error if the connection fails, this is probably not what you want.

I don’t get what the call_delay is all about.[/quote]

By “move the require statement out” you mean I should call it before the function, and is the same true for the assert statement too (whatever that is ?) ?

[code]local socket = require(“socket”)
c = assert(socket.connect(host, 4002))

function send_matrix_hex_command(…)
host = “192.168.1.204”
c:settimeout(5)
local sres, serr = c:send(string.char(…))
local data, rerr = c:receive(205)
print (“Receive:”, data, rerr)
c:close()

luup.call_delay(“matrix_reply_interpretation”, 1)

end[/code]

I’m assuming i need to wait at least a second for the response to be ready for the matrix_reply_interpretation function to then be called to do it’s thing and analyse the results ? Is that not the case ?

This is what I was asking previously. If you want to make a new connection each time, you need the connect within the function. I wouldn’t use assert. As it is you’ve got the variable ‘host’ undefined at the point it’s used.

I’m not sure that this very iterative approach is quite the way forward.

I’m just a simple man, with limited (bit thankfully improving) Lua abilities - i’m not sure what is the best way - step by step felt the best :slight_smile:

The code structure we’re now discussing has evolved quite a bit from where I started - which is good as it helps me to learn. But it must be painful for everyone else reading this thread :slight_smile:

Tell you what - I’ll try and pull the whole thing together (and sadly I only get pocket of time to focus on tweaking things on my Vera due to myjob) - I will try to keep it simpler too for now and look to make a new connection every time.

Ok, I think I have all the elements now, I’m just not able to link/pass elements between the functions - Help :frowning:

To try and keep this simple - the workflow aims to be - 1) HDMI Matrix command sent, 2) response from matrix captured/assessed, and 3) appropriate message presented.

[code]local function send_command_to_hdmi_matrix(…)
local socket = require(“socket”)
host = “192.168.1.204”
c = assert(socket.connect(host, 4002))
c:settimeout(5)
local sres, serr = c:send(string.char(…))
local data, rerr = c:receive(205)
local result = data: match “Status code:(%d+)” or “error”
print (“Receive:”, data, rerr)
print (“” …result)
c:close()
end

local function interpret_reply_from_hdmi_matrix()
message = {
[“error”] = “Something else happened, it likely failed”,
[“100011111110101”] = “Input 1 was selected successfully for Output A”,
[“100021111110101”] = “Input 2 was selected successfully for Output A”,
[“100031111110101”] = “Input 3 was selected successfully for Output A”,
[“100041111110101”] = “Input 4 was selected successfully for Output A”,
[“100024231110101”] = “Port scan completed successfully”,
– extend this with new codes when needed
}
end

local function present_results_of_hdmi_matrix_interaction()

print (message[result] or “Unexpected status code received”)

local handle = luup.task("Octava HDMI Matrix: " … result, 1, “Vera”, -1)
luup.call_delay(“clearTaskMsg”,2,handle)

– Help - I need the luup.task to use the text that’s looked up via the interpret_reply_from_hdmi_matrix function e.g ““Input 1 was selected successfully for Output A”,”

end

local function clearTaskMsg(strHandle)
luup.task(“”,4,“”,tonumber(strHandle))
end

send_command_to_hdmi_matrix(0x02,0x32,0x31,0x32,0x03)
[/code]

Well, nearly there. You just had to link it all together with the appropriate parameter passing…

local host = "192.168.1.204"
local port  = 4002

local socket = require("socket")


local function send_command_to_hdmi_matrix(...)
   local c = assert(socket.connect(host, port))
   c:settimeout(5)
   local sres, serr = c:send(string.char(...))
   local data, rerr = c:receive(205)
   local result = data: match "Status code:(%d+)" or "error"
   print ("Receive:", data, rerr)
   print ("" ..result)
   c:close()
   return result    -- return the result
end

local function interpret_reply_from_hdmi_matrix(status)
  local message = {
     ["error"] = "Something else happened, it likely failed",
     ["100011111110101"] = "Input 1 was selected successfully for Output A",
     ["100021111110101"] = "Input 2 was selected successfully for Output A",
     ["100031111110101"] = "Input 3 was selected successfully for Output A",
     ["100041111110101"] = "Input 4 was selected successfully for Output A",
     ["100024231110101"] = "Port scan completed successfully",
     -- extend this with new codes when needed
   }
  return message[status] or "Unexpected status code received"
end

local function present_results_of_hdmi_matrix_interaction(result)
  print (result)
  local handle = luup.task("Octava HDMI Matrix: " .. result, 1, "Vera", -1)
        luup.call_delay("clearTaskMsg",2,handle)
end

function clearTaskMsg(strHandle)  -- this HAS to be non-local for call_delay to work
  luup.task("",4,"",tonumber(strHandle))
end

-- here's your main 'workflow'

local status = send_command_to_hdmi_matrix(0x02,0x32,0x31,0x32,0x03)
local message = interpret_reply_from_hdmi_matrix(status)
present_results_of_hdmi_matrix_interaction(message)

-- done

Wow !! - It works … ;D (Thanks so much)

I have to be honest, I’m struggling to understand how all the functions are connected, plus it looks like you have done it so it sort of reverse order ?

It seems you start off by calling the function called present_results_of_hdmi_matrix_interaction(message) outright - which in turn calls the function assigned to ‘message’ which is interpret_reply_from_hdmi_matrix(status) - and then that calls the final function which has been assigned to ‘status’, and has the required attribute in it status = send_command_to_hdmi_matrix(0x02,0x32,0x31,0x32,0x03)

Am i close ?

No, not at all.

The functions, which can be defined in any order, are called by the sequence I labelled as your workflow, at the bottom of the code.

Definitely time to learn Lua programming properly by reading this: Programming in Lua