Getting Dynamic Data in Lua - c#

I have built an Instrumentation Cluster with the NVIDIA UI Composer Studio.
To control animations it uses the Lua Scripting Language. Now I'm really new to Lua and my question is the following:
I've got the following Code sequence in Lua which controls my Speedometer:
self.vehicleSpeedData = {} -- A lot of Values are put in here
function self.speedSim( inFrame, theController )
local timeInSec, dummy
timeInSec, dummy = getElapsedTime()
-- data based on 60fps
actualFrameNumber = math.floor(timeInSec * 60)
local theSample = ((actualFrameNumber-1) % #self.vehicleSpeedData) + 1
theController.value = self.vehicleSpeedData[theSample] *0.06
end
The array in this example is empty. So as you can see, the function reads out the Values of the Array.
But what I need is, that I can get this Data from an external Source (Such as RS232 or CAN Simulation) ... What I want to try is, if I can put Data from C# for example to that Lua Script.
It is hard do explain what I exactly want. My idea is that this Lua Script above listens and reads Data which I dynamically read in C# from my Data Source.
Thanks a lot for your help. This work is for my Bachelors Degree and I'm stuck at this point a long time and I'm nearly out of ideas.

It all kind of depends on what that Nvidia thing exposes to the user (in terms of API, and Lua base libraries).
Assuming it is available, you can use io.read to read in a datafile (say a csv) from another source, then parse it yourself into your table. If you can preprocess the file to have a valid Lua syntax (eg prepend a return {, have values separated by , , and end with a }, you could directly load the string with loadstring).
If they allow you to, you could use external libraries for interfacing with RS232, Excel, sockets, whatever.
PS: it's Lua not LUA (not an acronym, but the Portuguese noun for Moon ;))
Edit: example mkfifo
so in Linux it goes like this: make a fifo with mkfifo fff and feed it something echo ' '> fff to prevent Lua from blocking.
In Lua:
fh=io.open('fff','rb')
while true do
res = fh:read()
if res then
print(res)
end
end
Whatever cat into fff (eg cat 10 > fff) will come out in Lua. this way you can read out any available values and use them at each run of your function.
Another option is using standard input, but I'm not sure whether this composer thing lets you.

I hope your Bachelor's Degree turned out alright, what with this response being 1.5 years too late. :) Nonetheless:
As a member of the UI Composer team and a fellow Lua scripter, one technique I use often for streaming external data and events asynchronously into the runtime is to use the Lua Socket library. I have written an abstraction layer on top of it as a UIC behavior.
-- Expected message structure:
-- "<numbytes>,<optionaltag>\n<bytesofmessage>"
-- e.g. "11,simple\nHello World"
-- e.g. "40,\nThis has no tag,\nbut does have a newline"
local ok,socket = pcall(require,'socket')
if not ok then
output("Error loading socket: "..socket)
else
local output = output or print
local sscallbacks = {} -- indexed by simplesocket instance, then tag
SimpleSocket = {}
local SSMeta = {}
SSMeta.__index=SSMeta
function SimpleSocket:server(port,ip,timeout)
return self:create('server',port,ip,timeout)
end
function SimpleSocket:client(port,ip,timeout)
return self:create('client',port,ip,timeout)
end
function SimpleSocket:create(kind,port,ip,timeout)
if not port then port = 51423 end
if not ip then ip = '*' end
if not timeout then timeout = 10 end
local ss = setmetatable({
kind = kind,
ip = ip,
port = port,
timeout = timeout/1000,
queue = {}
},SSMeta)
sscallbacks[ss] = {}
return ss
end
function SSMeta:destroy()
if self.socket then self.socket:close() end
callbacks[self] = nil
end
function SSMeta:onData(callback,tag)
self:setCallback('handler',callback,tag)
end
function SSMeta:toEncode(callback,tag)
self:setCallback('encoder',callback,tag)
end
function SSMeta:toDecode(callback,tag)
self:setCallback('decoder',callback,tag)
end
function SSMeta:setCallback(type,callback,tag)
if not tag then tag = "" end
if not sscallbacks[self][tag] then sscallbacks[self][tag] = {} end
sscallbacks[self][tag][type] = callback
end
function self:onUpdate()
self:sendQueuedMessages()
self:receiveMessages()
end
function SSMeta:createSocket()
output("Creating new "..self.kind.." socket to "..self.ip..":"..self.port)
if self.kind=='server' then
self.socket = assert(socket.bind(self.ip,self.port))
self.socket:settimeout(self.timeout)
else
self.socket = assert(socket.connect(self.ip,self.port))
end
end
-- Attempts to send all messages from the queue
function self:sendQueuedMessages()
for ss,_ in pairs(sscallbacks) do
while ss.queue[1] do
if ss:sendMessage(message[1]) then
table.remove(ss.queue,1)
else
-- don't attempt any later messages, since ordering may be important
return
end
end
end
end
function self:receiveMessages()
for ss,callbacks in pairs(sscallbacks) do
if ss.kind=='client' then
if not ss.socket then ss:createSocket() end
ss.socket:settimeout(0) -- non-blocking for first byte
local char1, err = ss.socket:receive(1)
ss.socket:settimeout(ss.timeout) -- go back to blocking
if not char1 then
-- probably just timed out
else
local header, err = ss.socket:receive('*l')
if not header then
output(err)
else
header = char1..header
local comma = header:find(',')
local bytes = tonumber(header:sub(1,comma-1))
local tag = header:sub(comma+1)
local data,err = ss.socket:receive(bytes)
if not data then
output(err)
else
if callbacks[tag] and callbacks[tag].decoder then
data = callbacks[tag].decoder(data)
elseif callbacks[true] and callbacks[true].decoder then
data = callbacks[true].decoder(data)
end
if callbacks[tag] and callbacks[tag].handler then
callbacks[tag].handler(data)
elseif callbacks[true] and callbacks[true].handler then
callbacks[true].handler(data)
end
end
end
end
end
end
end
function SSMeta:send(data,tag)
return self:sendMessage(self:encodeMessage(data,tag))
end
function SSMeta:ensureSend(data,tag)
local message = self:encodeMessage(data,tag)
if not self:sendMessage(message) then
table.insert(self.queue,message)
end
end
-- Internal only; use send() or ensureSend()
function SSMeta:sendMessage(formattedMessage)
if not self.socket then self:createSocket() end
if not self.client then self.client = self.socket:accept() end
if self.client then
local lastbyte,err = self.client:send(formattedMessage)
if lastbyte then
-- TODO: verify that all bytes were sent
return true
else
output(err)
self.client:close()
self.client = nil
end
else
-- No client connected before the timeout
end
end
function SSMeta:encodeMessage(data,tag)
data = tostring(data)
local callbacks = sscallbacks[self]
if callbacks[tag] and callbacks[tag].encoder then
data = callbacks[tag].encoder(data)
elseif callbacks[true] and callbacks[true].encoder then
data = callbacks[true].encoder(data)
end
return tostring(#data)..","..(tag or "").."\n"..data
end
end
This allows multiple different systems to communicate on the same socket, with different tagged communication, and possibly different encoders/decoders to serialize/deserialize the data.
On the receiving end this is used for example like this:
local ss = require 'SimpleSocket'
local client = ss:client()
client:onData(function(d) print("Client got *: "..d) end,true)

Related

Trigger .exe upon receiving email from sender

I've written a C# application with .net framework. The purpose is to request data from an online spreadsheet app, do stuff with it, then send back updated data.
I think the best way to trigger the exe would be to use webhooks/callbacks, but I gather this would require runnning my program on a web sever with an external IP address.
Rather than periodically polling the spreadsheet app I would like the app to send emails to a specified account upon certain data changes. Upon receiving the email, VBA code checks that the email is from the app then runs the executable.
To run exe on receipt of email:
Private Sub Application_NewMail()
Dim path As String
Dim shl As Variant
path = "C:\Users\***\Desktop\SmartPlugin.exe"
shl = Shell(path, 1)
End Sub
How do I check the sender? The examples I found online loop through all emails but what I'm after is a method of returning the last email received.
Use Application.NewMailEx event instead - it passes the entry id of the new message as the parameter. Use that entry id to call Application.Session.GetItemfromID.
Got it working.
Private Sub Application_NewMail()
Dim objN As NameSpace
Dim objF As MAPIFolder
Set objN = GetNamespace("MAPI")
Set objF = objN.GetDefaultFolder(olFolderInbox)
Set mlItems = objF.Items
mlItems.Sort "CreationTime", True
Set mlItem = mlItems.Item(1)
Dim path As String
Dim shl As Variant
Dim sndString As String
sndString = CStr(mlItem.SenderName)
If InStr(1, sndString, "SmartSheet", vbTextCompare) > 0 Then
path = "C:\Users\Alex Rose\Desktop\SmartPlugin.exe"
shl = Shell(path, 1)
End If
End Sub

No return value of the PS command "net file" while accessing the powershell remotely with .NET

I've built a Sub to interact with PowerShells on other servers within my domain. I never needed any return values of the PS console so far. As I want to control the Computer Management on my FileShare I need the list of all open file-sessions (net file) as you need the session ID to close it with net file "ID" /close.
This is the modified Sub:
Public Sub remoteMSPowershell(ByVal Script As String)
Dim shellUri As String = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell"
Dim newCred As PSCredential = DirectCast(Nothing, PSCredential)
Dim connectionInfo As WSManConnectionInfo = New WSManConnectionInfo(False, "SVR2012r2-file", 5985, "/wsman", shellUri, newCred)
Using Runspace As Runspace = RunspaceFactory.CreateRunspace(connectionInfo)
Runspace.Open()
Dim pipeline As Pipeline = Runspace.CreatePipeline(Script)
Dim results = pipeline.Invoke()
For Each outputItem As PSObject In results
Console.WriteLine(outputItem.Members("Path").Value)
Console.WriteLine(outputItem.Members("Id").Value)
Next
Console.ReadLine()
End Using
End Sub
While outputItem.BaseObject.ToString() gives you 1:1 the same output as you see on the PS console, I only need the Attributes "Path" and "Id".
If I run this Sub on commands like Get-Command, Get-Service and so on it'll form a column with the values of the specified attribute. However if I run it on net file the script fails with a NullReferenceException. The only thing that I can imagine is that .NET is only capable of filtering to members (attributes) of Cmdlets, but I didn't find any reference on that.
EDIT (24.05.16): It doesn't work for all the NET.exe - commands.. Is there another way to read the return values out or even another method to get the return values using PS or .NET?
EDIT (26.05.16): I've been working on two work-arounds.. Both seem to be a dead end though:
Unify the results from outputItem.BaseObject.ToString() and form Arrays of the "Id" and "Path" column. This won't work as the only indicator to split the rows into attributes are (blancs). You can imagine what happens if the File-Path contains a blanc..
A more clean work-around would be to query the desired attributes already in PowerShell like the answers to this SO question. However none of the given answers works for me as you can't query NET commands. See example:

Send/Receive http request/response from MS Word addin

I'm trying to write a add-in for MS Word. Is it possible to for a MS Word add-in to listen on specific port and send/receive http request/response? Will there be a firewall between MS Word and an application that is running outside of Word?
An Office AddIn can make HTTP requests without triggering a firewall issue (using Windows Firewall in its default configuration), but it can't listen without triggering firewall issues.
If you're making requests from inside word to a service you have living outside of Word that service may encounter Firewall issues while listening on a port.
Windows Firewall, by default blocks incoming requests. Windows Firewall is included with all versions of Windows XP SP2 or later.
See MSDN for more.
Additionally,
Function GetRateCBR(dDate As Date) As String
Dim sUrlRequest, intTry As Integer, _
strResponse As String
Dim oXMLHTTP As Object
Dim oResponse As Object
Set oResponse = CreateObject("MSXML2.DOMDocument")
'Build URL for request
sUrlRequest = _
"http://www.cbr.ru/scripts/XML_dynamic.asp?date_req1=" _
& Format(dDate, "dd.mm.yyyy") _
& "&date_req2=" & Format(dDate, "dd.mm.yyyy") _
& "&VAL_NM_RQ=" & "R01235"
'Try to get a response, 10 tries
intTry = 1
Do Until intTry > 10
Set oXMLHTTP = CreateObject("MSXML2.XMLHTTP")
oXMLHTTP.Open "GET", sUrlRequest, False
oXMLHTTP.send
If oXMLHTTP.Status = 200 Then
If oResponse.loadXML(oXMLHTTP.responseText) Then _
Exit Do
End If
If Not oXMLHTTP Is Nothing Then oXMLHTTP.abort: _
Set oXMLHTTP = Nothing
DoEvents
intTry = intTry + 1
Loop
If Not oXMLHTTP Is Nothing Then oXMLHTTP.abort: _
Set oXMLHTTP = Nothing
If intTry <= 10 Then
GetRateCBR = Mid$(oResponse.Text, 3)
End If
If Not oResponse Is Nothing Then oResponse.abort: _
Set oResponse = Nothing
End Function
Example via Access Blog

Ruby on Rails TCPSocket freezes in Client Application while loop using gets

I am following the tutorial: http://www.tutorialspoint.com/ruby/ruby_socket_programming.htm and using it to set up the "Simple Client".
The code is as follows:
require 'socket' # Sockets are in standard library
hostname = 'localhost'
port = 2000
s = TCPSocket.open(host, port)
while line = s.gets # Read lines from the socket
puts line.chop # And print with platform line terminator
end
s.close # Close the socket when done
When I execute a single s.gets statement, I get the first response line from the server. When I do s.gets again, I get the second response line, etc. However, the moment I get to the end of the response and do s.gets then the program goes into a frozen state. (This is if I test the above program from the Rails Console). The same happens when I run the code - the freeze occurs when the while statement does the final s.gets. Any ideas on how to solve this?
For information: The server is a C# server that sends response messages using the following C# code (each line of the response uses the same code and the final line in the response is not treated any differently from the other lines):
socket.Send(Encoding.ASCII.GetBytes(msg + "\r\n"));
So I am thinking the client freezes because it probably doesn't interpret the servers "end of response" properly. Any ideas?
It seems like there is a problem with reading the nil value from the socket. Still not sure why this happens, but using a selector as suggested in Recovering from a broken TCP socket in Ruby when in gets() solves the problem very nicely (would have upvoted his answer, but don't have enough rep).
Solution I followed was to add the selector in a method as follows:
def next_line_readable?(socket)
readfds, writefds, exceptfds = select([socket], nil, nil, 0.1)
p :r => readfds, :w => writefds, :e => exceptfds
readfds #Will be nil if next line cannot be read
end
and then in main program to use it like:
socket = TCPSocket.open(host, port)
while next_line_readable?(socket)
puts socket.gets.chop
end
Try this am writing a tcp client and server in ruby
def sendto(str)
begin
socket.write(str)
puts "Sent Message to:" + socket.peeraddr[2]
rescue Exception=>e
puts red("Error Make sure the target is connected")
end
end
Check it out works perfectly until 4hours then disconnects
Connection to ruby based server
Try using the socket.read or socket.recvfrom methods instead of socket.gets.
Is it the same behavior?
use recv instead of gets
server = TCPServer.new 53492 # Server bound to port 53492
loop do
client = server.accept
ansiString = client.recv(100).chomp
client.puts result
client.close
end
you can convert it into the thread also as:
server = TCPServer.new 53492 # Server bound to port 53492
Thread.start(server.accept) do |client|
loop do
client = server.accept
ansiString = client.recv(100).chomp
client.puts result
client.close
end
end

Cannot access SystemListView contents using Managed Win32 API's

I am using the ManagedWindows API in a C# environment:
http://mwinapi.sourceforge.net/
In the past I have successfully scraped the contents of listbox-like parts of other running programs using the code below, where I iterate through key/value pairs to find the list items. For this particular list of items, however, I can get an accurate number of items, but the value is always null!
Using this:
TargetMidWindow.Content.ComponentType
I have discovered that the list I am having issues with is a 'listview' whereas the other windows I have had success with are 'detailslistview' in case it matters. Below is the code I have for finding the data I want, which is almost identical to my other successful code with the exception of altering the search terms I used. Also, in case its relevant, the program I'm trying to pull data out of is MetaTrader4, and I've been able to scrape data off other parts of the program successfully.
// Find the main window
SystemWindow[] TopLevel = SystemWindow.AllToplevelWindows;
SystemWindow TargetTopWindow = SystemWindow.ForegroundWindow;
foreach (SystemWindow SearchWindow in TopLevel)
{
string Title = SearchWindow.Title;
if (Title.Contains("MetaTrader"))
{
TargetTopWindow = SearchWindow;
break;
}
}
// Find the section where positions are contained
SystemWindow[] MidLevel = TargetTopWindow.AllDescendantWindows;
SystemWindow TargetMidWindow = SystemWindow.ForegroundWindow;
foreach (SystemWindow SearchWindow in MidLevel)
{
string ClassName = SearchWindow.ClassName;
if (ClassName.Contains("SysListView32"))
{
SystemWindow ParentWindow = SearchWindow.Parent;
if ((ParentWindow.Title.Contains("Terminal")))
{
TargetMidWindow = SearchWindow;
}
}
}
// Get the positions
Dictionary<string, string> RawValues = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> KVP in TargetMidWindow.Content.PropertyList)
{
string key = KVP.Key;
string value = KVP.Value;
}
Is there something special I need to do so that I do not get 'null' values for each list item?
Thanks!
Bill
Hmya, wrapping Windows messages with a friendly API isn't that difficult. Windows Forms would be a good example. But that has a knack for running into a very solid wall once you start doing this with another process.
The specific message you need in order to read ListView items is LVM_GETITEM. That's one of those solid wall messages. The LPARAM argument you pass to SendMessage() needs to be a pointer to an LVITEM structure. The control fills in the fields in that structure. Problem is, the pointer you pass is only valid in your process, not the process who owns that window.
Fixing this takes a great deal of hackery. You have to allocate memory that's valid inside that process. That takes VirtualAllocEx() and ReadProcessMemory(). Plus all the glue calls you need to make these work. I assume that this library you are using is not taking care of this. Easy to find out, grep the source code files for these API function names.
If you want to find the correct handle to a particular SysListView32 window, you need to start with the right window hierarchy. From the code snippet, it doesn't appear that you're actually finding the correct handle to retrieve a quote from the SysListView32 window. This is why you're receiving null values back. You would do well to run spy++ and determine the correct windows structure of the Metatrader terminal for your specific broker and build. I've found that the classes are different between builds for some of the windows, and also between some brokers, though to a lesser extent.
You're looking for the specific quote window hierarchy like this:
Metatrader -> Market Watch -> Market Watch -> SysListView32
By contrast, currently you're looking here in your code:
Metatrader -> Terminal -> (many sub-windows with SysListView32 class)
Where each level to the right is a child window of the window to the left.
Find the parent "Metatrader" window then chain down looking for the child window until you get to SysListView32. If you use spy++ you can read the class for the SysListView32 parent window (market watch), and use that to enumerate the windows to find the correct SysListView32 window. FYI the correct Market Watch class name for build 419 is:
Afx:00400000:b:00010003:00000000:00000000
Once you find the correct window, you may be able to extract its contents using your current component. I haven't tried that and am looking to port some code from VB6 from a ListView module that does in fact involve epic hackery. ;) I may take a look at the .NET Managed Windows API to see if this can help make the process simpler.
But in the mean time, if you do have to go low-level, the following VB6 source should help you get an idea of what is involved. This is fairly advanced material so good luck!
Public Function GetListviewItem(ByVal hWindow As Long, ByVal pColumn As Long, ByVal pRow As Long) As String
Dim result As Long
Dim myItem As LV_ITEMA
Dim pHandle As Long
Dim pStrBufferMemory As Long
Dim pMyItemMemory As Long
Dim strBuffer() As Byte
Dim index As Long
Dim tmpString As String
Dim strLength As Long
Dim ProcessID As Long, ThreadID As Long
ThreadID = GetWindowThreadProcessId(hWindow, ProcessID)
'**********************
'init the string buffer
'**********************
ReDim strBuffer(MAX_LVMSTRING)
'***********************************************************
'open a handle to the process and allocate the string buffer
'***********************************************************
pHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, ProcessID)
pStrBufferMemory = VirtualAllocEx(pHandle, 0, MAX_LVMSTRING, MEM_COMMIT, PAGE_READWRITE)
'************************************************************************************
'initialize the local LV_ITEM structure
'The myItem.iSubItem member is set to the index of the column that is being retrieved
'************************************************************************************
myItem.mask = LVIF_TEXT
myItem.iSubItem = pColumn
myItem.pszText = pStrBufferMemory
myItem.cchTextMax = MAX_LVMSTRING
'**********************************************************
'write the structure into the remote process's memory space
'**********************************************************
pMyItemMemory = VirtualAllocEx(pHandle, 0, Len(myItem), MEM_COMMIT, PAGE_READWRITE)
result = WriteProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
'*************************************************************
'send the get the item message and write back the memory space
'*************************************************************
result = SendMessage(hWindow, LVM_GETITEMTEXT, pRow, ByVal pMyItemMemory)
result = ReadProcessMemory(pHandle, pStrBufferMemory, strBuffer(0), MAX_LVMSTRING, 0)
result = ReadProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
'**************************************************
'turn the byte array into a string and send it back
'**************************************************
For index = LBound(strBuffer) To UBound(strBuffer)
If Chr(strBuffer(index)) = vbNullChar Then Exit For
tmpString = tmpString & Chr(strBuffer(index))
Next index
tmpString = Trim(tmpString)
'**************************************************
'deallocate the memory and close the process handle
'**************************************************
result = VirtualFreeEx(pHandle, pStrBufferMemory, 0, MEM_RELEASE)
result = VirtualFreeEx(pHandle, pMyItemMemory, 0, MEM_RELEASE)
result = CloseHandle(pHandle)
If Len(tmpString) > 0 Then GetListviewItem = tmpString
End Function

Categories

Resources