Most simple network protocol for a remote function call? - matlab

I am looking for the most simple protocol to program a remote function call, e.g. from Matlab to Julia.
[out1, out2, ...] = jlcall(socket, fname, arg1, arg2, ...);
fname is a string, all other input and output variables are numerical arrays (or other structures known to both sides) on Linux, Windows as option.
Client: open connection, block, pack and transmit
Server: receive, unpack, process, pack and transmit back
Client: receive, unpack, close connection and continue
The solutions I've seen (tcp, zmq) were built with old versions and do no longer work.
Protocol could (should?) be limited to do the pack/transmit - receive/unpack work.
UPDATE
Here is what I have come up with using pipes:
function result = jlcall(varargin)
% result = jlcall('fname', arg1, arg2, ...)
% call a Julia function with arguments from Matlab
if nargin == 0 % demo
task = {'foo', 2, 3}; % demo fun, defined in jsoncall.jl
else
task = varargin;
end
% create pipe and write function and parameter(s) to pipe
pipename = tempname;
pipe = java.io.FileOutputStream(pipename);
pipe.write(uint8(jsonencode(task)));
pipe.close;
% run Julia and read result back
system(sprintf('julia jsoncall.jl %s', unixpath(pipename)))
fid = fopen(pipename, 'r');
c = fread(fid);
result = jsondecode(char(c'));
fclose(fid);
function path_unix = unixpath(path_pc)
%convert path to unix version
path_unix = path_pc;
path_unix(strfind(path_unix,'\'))='/';
# jsoncall.jl
using JSON3 # get JSON3.jl from repository first
function foo(a,b) # demo function
a+b, a*b
end
jsonfile = ARGS[1] # called as > julia jsoncall.jl <json_cmdfile>
io = open(jsonfile, "r") # open IOStream for read
data = read(io) # read UTF8 data from stream
close(io) # close stream
cmd = JSON3.read(String(data)) # unpack stream into [fun, farg] array
fun = Symbol(cmd[1]) # first element is Julia function name,
result = #eval $fun(cmd[2:end]...) # others are function arguments
io = open(jsonfile, "w") # open IOStream for write
write(io, JSON3.write(result)) # (over-)write result back to stream
close(io) # close stream
Open points:
my first use of pipes/streams
output formatting: where Julia outputs a tuple of two, Matlab creates an an nx2 array.
replace json by msgpack for performance, might help with type formatting as well.
Your comments are welcome!

Here is a stripped down way of doing it. If you are going to vary your functions and arguments, a REST as in the comments server is going to be more flexible and less likely to pose a security risk (as you are eval()ing arbitrary code in some cases).
#server code
using Sockets
const port = 6001
const addr = ip"127.0.0.1"
const server = listen(addr, port)
while true
try
#info "Server on $port awaiting request..."
sock = accept(server)
#info "Server connected."
msg = strip(readline(sock))
#info "got message $msg"
fstr, argstr = split(msg, limit=2)
x = parse(Float64, argstr) # or other taint checks here...
ans = eval(Meta.parse(fstr * "($x)"))
#info "server answer: $ans"
write(sock, "$ans\n")
catch y
#info "exiting on condition: $y"
end
end
# client code
using Sockets
port = 6001
server = ip"127.0.0.1"
sock = connect(server, port)
#info "Client connected to $server"
func = "sinpi"
x = 0.5
#info "starting send"
write(sock, "$func $x\n")
flush(sock)
#info "flushed send"
msg = strip(readline(sock)) # read one line of input and \n, remove \n
ans = parse(Float64, msg)
println("answer is $ans")
close(sock)

Related

How can I create an animated plot from incoming serial port data

I want to live plot incoming data from a serial port without writing and reading data from a file. I understand that it's necessary to run a thread where the serial data is coming in on the fly. While the thread is running the animation.FuncAnimation should grab the serial data and render the plot each time.
That's where I can't figure out how to code the handover to get the animation to work.
import serial
import threading
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import itertools
def thread_data_gen():
i = 0
counter = itertools.count()
ser = serial.Serial('/dev/tty.SLAB_USBtoUART', 1000000, timeout=1) # Open port and read data.
ser.reset_input_buffer() # Flush input buffer, discarding all its contents.
while True:
ser_bytes = ser.readline() # Read serial data line by line
# Incoming lines in Format -> b'$ 32079 32079 32079 32079 32079 32079 32079 32079;\r\n'
# Each value represents a data channel which can measure torque, temperatur,...
# Right now all values are set to torque which is why they have the same values.
# 8 data channels available
decoded_bytes = ser_bytes.decode('utf-8')
decoded_bytes = decoded_bytes.lstrip('$').rstrip().split()
# Re-format to ['32274', '32274', '32274', '32274', '32274', '32274', '32274', '32274;']
if i == 0:
i += 1
continue
# Skip first read-out because the first transmitted line is empty. Don't know the reason.
t = next(counter) # Create values for x-axis
y = decoded_bytes[0] # Read channel 1 values for y-axis
yield t, y # Was thinking about a generator type of return but that's not working
def run(data): # Handing over yield data from thread_data_gen()
t, y = data # TypeError:"cannot unpack non-iterable int object" I don't know how to fix this.
# I have to convert the while loop in the running thread to something where I get iterable int data.
# That's where I don't see the solution
xdata.append(t) # Adding data to list for x-values
ydata.append(y) # Adding data to list for y-values
line.set_data(xdata, ydata) # Creating data for hand-over to plt
return line,
if __name__ == '__main__':
dgen = threading.Thread(target=thread_data_gen)
dgen.start()
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.grid()
xdata, ydata = [], []
ani = animation.FuncAnimation(fig, run, interval=100)
plt.show()
Update
I just solved my own problem with:
# A sensor upgraded coupling in a power transmission system provides data per bluetooth.
# Data: Torque, force, temperature and acceleration (x-y-z-axis).
# A hardware gateway receives the data with an antenna.
# The purpose of this code is to read and plot out serial data from that hardware gateway
# which is linked via USB-C to the laptop. A USB-C to UART software bride,
# makes the data as serial port on MacOS or Windows available.
import time
import serial
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import itertools
def data_gen():
i = 0
counter = itertools.count() # Create counter
ser = serial.Serial('/dev/tty.SLAB_USBtoUART', 1000000, timeout=1) # Open port and read data.
ser.reset_input_buffer() # Flush input buffer, discarding all its contents.
while True:
# Read serial data line by line.
# Incoming lines in Format -> b'$ 32079 32079 32079 32079 32079 32079 32079 32079;\r\n'
# Each value represents a data channel which can measure torque, temperatur, ...
# Right now all values are set to torque which is why they have the same values.
# 8 data channels are available
ser_bytes = ser.readline()
decoded_bytes = ser_bytes.decode('utf-8')
# Re-format to list ['32274', '32274', '32274', '32274', '32274', '32274', '32274', '32274;']
decoded_bytes = decoded_bytes.lstrip('$').rstrip().split()
# Skip first read-out because the first transmitted line is empty. Don't know the reason.
if i == 0:
i += 1
continue
t = next(counter) # Create values for x-axis
y = decoded_bytes[0] # Read channel 1 values for y-axis
yield t, float(y) # Yield back x and y values for plot
def run(data):
x, y = data
y_cal = round(y * 0.00166, 1) #
xdata.append(x) # Adding data to list for x-values
ydata.append(y_cal) # Adding data to list for y-values
line.set_data(xdata, ydata) # Creating a data set for hand-over to plot
return line,
if __name__ == '__main__':
fig, ax = plt.subplots() # Setup figure and axis
line, = ax.plot([], [], lw=2) # Setup line
ax.set_ylim(40, 80) # Set limitation in y
ax.set_xlim(0, 1000) # Set limitation in x
ax.grid() # Create grid
xdata, ydata = [], [] # Create empty lists
# Frames: Receives the generated data from the serial connection
# Run: Provides the line data
ani = animation.FuncAnimation(fig, run, frames=data_gen, blit=True, interval=10)
plt.show()

Issue sending/receiving data over serial connection using MATLAB

I recently connected a reactor control tower through a serial 'COMMS' port to my computer (serial to USB). It seems to create a connection on the COM4 port (as indicated on the control panel 'devices and printers' section). However, it always gives me the following message when i try to 'fwrite(s)' or 'fscanf(s)'.
Warning: Unsuccessful read: The specified amount of data was not returned within the Timeout period..
%My COM4
s = serial('COM4');
s.BaudRate = 9600;
s.DataBits = 8;
s.Parity ='none';
s.StopBits = 1;
s.FlowControl='none';
s.Terminator = ';';
s.ByteOrder = 'LittleEndian';
s.ReadAsyncMode = 'manual';
% Building write message.
devID = '02'; % device ID
cmd = 'S'; % command read or write; S for write
readM = cell(961,3);% Read at most 961-by-3 values filling a 961–by–3 matrix in column order
strF = num2str(i);
strF = '11'; %pH parameter
strP = '15'; %pH set point
val = '006.8'; %pH set value
msg_ = strcat('!', devID, cmd, strF, strP, val);%output the string
chksum = dec2hex(mod(sum(msg_),256)); %conversion to hexdec
msg = strcat(msg_,':', char(chksum), ';');
fopen(s); %connects s to the device using fopen , writes and reads text data
fwrite(s, uint8(msg)); %writes the binary data/ Convert to 8-bit unsigned integer (unit8) to the instrument connected to s.
reply=fscanf(s); %reads ASCII data from the device connected to the serial port object and returns it to reply, for binary data use fread
fclose(s); %Disconnect s from the scope, and remove s from memory and the workspace.
This leads me to believe that the device is connected but is not sending or receiving information and I am unsure as to how I can configure this or even really check if there is communication occurring between the tower and my computer.

interrupt a TCP-IP callback function in matlab

Recently wrote code that establishes a connection between two instances of matlab. I can send messages through the TCP-IP connection which will execute code. Now I'm trying to setup the code to be interruptible as I would like to start/stop a function through TCP-IP. Problem though is that sending a second command does nothing until the function is completed. Is there a way to interrupt a TCP-IP callback function?
code:
classdef connectcompstogether<handle
properties
serverIP
clientIP
tcpipServer
tcpipClient
Port = 4000;
bsize = 8;
earlystop
end
methods
function gh = connectcompstogether(~)
% gh.serverIP = '127.0.0.1';
gh.serverIP = 'localhost';
gh.clientIP = '0.0.0.0';
end
function SetupServer(gh)
gh.tcpipServer = tcpip(gh.clientIP,gh.Port,'NetworkRole','Server');
set(gh.tcpipServer,'OutputBufferSize',gh.bsize);
fopen(gh.tcpipServer);
display('Established Connection')
end
function SetupClient(gh)
gh.tcpipClient = tcpip(gh.serverIP,gh.Port,'NetworkRole','Client');
set(gh.tcpipClient, 'InputBufferSize',gh.bsize);
set(gh.tcpipClient, 'BytesAvailableFcnCount',8);
set(gh.tcpipClient, 'BytesAvailableFcnMode','byte');
set(gh.tcpipClient, 'BytesAvailableFcn', #(h,e)gh.recmessage(h,e));
fopen(gh.tcpipClient);
display('Established Connection')
end
function CloseClient(gh)
fclose(gh.tcpipClient);
gh.tcpipClient = [];
end
end
methods
function sendmessage(gh,message)
fwrite(gh.tcpipServer,message,'double');
end
function recmessage(gh,h,e)
Message = fread(gh.tcpipClient,gh.bsize/8,'double');
if Message == 444
gh.Funwithnumbers();
elseif Message == 777
gh.earlystop = 1;
end
end
function Funwithnumbers(gh)
x=1;
while true
if x > 5000, break;end
if gh.earlystop == 1,break;end
x = x+1;
display(x)
end
end
end
end
for ease to understand code.
server
Ser = connectcompstogether;
ser.SetupServer();
ser.sendmessage(333);
Client
cli = connectcompstogether;
cli.SetupClient();
Update:
So after going through the web, I have found out based on this post that the tcpip callback cannot be interrupt. The post was in 2017 which means my 2016a version definitely cannot interrupt a callback.
So An update to my question, Is it possible to start a subprocess in matlab to run the function. I just want to use the callback to start code. If I can start a subprocess from the callback. Than I should be able to free up the main process and use tcpip to start/stop a function on a different computer.
Update 2:
So I tried to utilize parallel processing using the 'spmd' command but the problem still persisted.
function recmessage(gh,h,e)
Message = fread(gh.tcpipClient,gh.bsize/8,'double');
spmd
switch labindex
case 1
if Message == 444
gh.Funwithnumbers();
elseif Message == 777
gh.earlystop = 1;
end
end
end
end
You may use a timer object, which is convenient to delay the execution of some function.
t=timer('ExecutionMode','singleShot', 'StartDelay',0, 'TimerFcn',#myCallback);
start(t);
In this case, the StartDelay is 0, so myCallback will be almost immediately added to the queue of tasks to be processed by Matlab. The execution however will start only after the callback to the tcpip object has been completed. It will block the queue once started, however.
You may try something like:
properties
t=timer('ExecutionMode','singleShot', 'StartDelay',0, 'TimerFcn',#myCallback);
end
function tcpipCallback(gh,tcpObj,~)
message=fread(tcpObj,1,'double');
if message==444
if strcmp(get(t,'Running'),'on')
error('The function is running already');
else
set(gh.t,'UserData',false);
start(gh.t);
end
elseif message==777
set(gh.t,'UserData',true);
end
function myCallback(tObj,~)
ii=0;
while ii<5000
if get(tObj,'UserData'),break,end
ii=ii+1;
pause(.0001); %Pause to interrupt the callback; drawnnow might work too; or perhaps this is not needed at all.
end
end

Simpy: How can I represent failures in a train subway simulation?

New python user here and first post on this great website. I haven't been able to find an answer to my question so hopefully it is unique.
Using simpy I am trying to create a train subway/metro simulation with failures and repairs periodically built into the system. These failures happen to the train but also to signals on sections of track and on plaforms. I have read and applied the official Machine Shop example (which you can see resemblance of in the attached code) and have thus managed to model random failures and repairs to the train by interrupting its 'journey time'.
However I have not figured out how to model failures of signals on the routes which the trains follow. I am currently just specifying a time for a trip from A to B, which does get interrupted but only due to train failure.
Is it possible to define each trip as its own process i.e. a separate process for sections A_to_B and B_to_C, and separate platforms as pA, pB and pC. Each one with a single resource (to allow only one train on it at a time) and to incorporate random failures and repairs for these section and platform processes? I would also need to perhaps have several sections between two platforms, any of which could experience a failure.
Any help would be greatly appreciated.
Here's my code so far:
import random
import simpy
import numpy
RANDOM_SEED = 1234
T_MEAN_A = 240.0 # mean journey time
T_MEAN_EXPO_A = 1/T_MEAN_A # for exponential distribution
T_MEAN_B = 240.0 # mean journey time
T_MEAN_EXPO_B = 1/T_MEAN_B # for exponential distribution
DWELL_TIME = 30.0 # amount of time train sits at platform for passengers
DWELL_TIME_EXPO = 1/DWELL_TIME
MTTF = 3600.0 # mean time to failure (seconds)
TTF_MEAN = 1/MTTF # for exponential distribution
REPAIR_TIME = 240.0
REPAIR_TIME_EXPO = 1/REPAIR_TIME
NUM_TRAINS = 1
SIM_TIME_DAYS = 100
SIM_TIME = 3600 * 18 * SIM_TIME_DAYS
SIM_TIME_HOURS = SIM_TIME/3600
# Defining the times for processes
def A_B(): # returns processing time for journey A to B
return random.expovariate(T_MEAN_EXPO_A) + random.expovariate(DWELL_TIME_EXPO)
def B_C(): # returns processing time for journey B to C
return random.expovariate(T_MEAN_EXPO_B) + random.expovariate(DWELL_TIME_EXPO)
def time_to_failure(): # returns time until next failure
return random.expovariate(TTF_MEAN)
# Defining the train
class Train(object):
def __init__(self, env, name, repair):
self.env = env
self.name = name
self.trips_complete = 0
self.broken = False
# Start "travelling" and "break_train" processes for the train
self.process = env.process(self.running(repair))
env.process(self.break_train())
def running(self, repair):
while True:
# start trip A_B
done_in = A_B()
while done_in:
try:
# going on the trip
start = self.env.now
yield self.env.timeout(done_in)
done_in = 0 # Set to 0 to exit while loop
except simpy.Interrupt:
self.broken = True
done_in -= self.env.now - start # How much time left?
with repair.request(priority = 1) as req:
yield req
yield self.env.timeout(random.expovariate(REPAIR_TIME_EXPO))
self.broken = False
# Trip is finished
self.trips_complete += 1
# start trip B_C
done_in = B_C()
while done_in:
try:
# going on the trip
start = self.env.now
yield self.env.timeout(done_in)
done_in = 0 # Set to 0 to exit while loop
except simpy.Interrupt:
self.broken = True
done_in -= self.env.now - start # How much time left?
with repair.request(priority = 1) as req:
yield req
yield self.env.timeout(random.expovariate(REPAIR_TIME_EXPO))
self.broken = False
# Trip is finished
self.trips_complete += 1
# Defining the failure
def break_train(self):
while True:
yield self.env.timeout(time_to_failure())
if not self.broken:
# Only break the train if it is currently working
self.process.interrupt()
# Setup and start the simulation
print('Train trip simulator')
random.seed(RANDOM_SEED) # Helps with reproduction
# Create an environment and start setup process
env = simpy.Environment()
repair = simpy.PreemptiveResource(env, capacity = 1)
trains = [Train(env, 'Train %d' % i, repair)
for i in range(NUM_TRAINS)]
# Execute
env.run(until = SIM_TIME)
# Analysis
trips = []
print('Train trips after %s hours of simulation' % SIM_TIME_HOURS)
for train in trains:
print('%s completed %d trips.' % (train.name, train.trips_complete))
trips.append(train.trips_complete)
mean_trips = numpy.mean(trips)
std_trips = numpy.std(trips)
print "mean trips: %d" % mean_trips
print "standard deviation trips: %d" % std_trips
it looks like you are using Python 2, which is a bit unfortunate, because
Python 3.3 and above give you some more flexibility with Python generators. But
your problem should be solveable in Python 2 nonetheless.
you can use sub processes within in a process:
def sub(env):
print('I am a sub process')
yield env.timeout(1)
# return 23 # Only works in py3.3 and above
env.exit(23) # Workaround for older python versions
def main(env):
print('I am the main process')
retval = yield env.process(sub(env))
print('Sub returned', retval)
As you can see, you can use Process instances returned by Environment.process()
like normal events. You can even use return values in your sub proceses.
If you use Python 3.3 or newer, you don’t have to explicitly start a new
sub-process but can use sub() as a sub routine instead and just forward the
events it yields:
def sub(env):
print('I am a sub routine')
yield env.timeout(1)
return 23
def main(env):
print('I am the main process')
retval = yield from sub(env)
print('Sub returned', retval)
You may also be able to model signals as resources that may either be used
by failure process or by a train. If the failure process requests the signal
at first, the train has to wait in front of the signal until the failure
process releases the signal resource. If the train is aleady passing the
signal (and thus has the resource), the signal cannot break. I don’t think
that’s a problem be cause the train can’t stop anyway. If it should be
a problem, just use a PreemptiveResource.
I hope this helps. Please feel welcome to join our mailing list for more
discussions.

Moai: Graphics that reacts to commands via Sockets

I need a program that can create pre-defined shapes on screen according to that commands I send to it via TCP.
I'm trying to listen to a port and so that I can use them. Before waiting of a command (via network) I have the commands required to create a square (I plan to change its attributes via network commands)
The problem is it is not creating any graphics or opening the window as it should be..
require "socket"
require "mime"
require "ltn12"
host = "localhost"
port = "8080"
server, error = socket.bind(host, port)
if not server then print("server: " .. tostring(error)) os.exit() end
screen=MOAISim.openWindow ( "test", 640, 640 )
viewport = MOAIViewport.new (screen)
viewport:setSize ( 640, 640 )
viewport:setScale ( 640, 640 )
layer = MOAILayer2D.new ()
layer:setViewport ( viewport )
MOAISim.pushRenderPass ( layer )
function fillSquare (x,y,radius,red,green,blue)
a = red/255
b = green/255
c = blue/255
MOAIGfxDevice.setPenColor ( a, b, c) -- green
MOAIGfxDevice.setPenWidth ( 2 )
MOAIDraw.fillCircle ( x, y, radius, 4 ) -- x,y,r,steps
end
function onDraw ( )
fillSquare(0,64,64, 0,0,255)
end
scriptDeck = MOAIScriptDeck.new ()
scriptDeck:setRect ( -64, -64, 64, 64 )
scriptDeck:setDrawCallback ( onDraw)
prop = MOAIProp2D.new ()
prop:setDeck ( scriptDeck )
layer:insertProp ( prop )
while 1 do
print("server: waiting for client command...")
control = server:accept()
command, error = control:receive()
print(command,error)
error = control:send("hi from Moai\n")
end
It is waiting of the command from client at control = server:accept() but it is not opening up the graphics window as it should.. Is there any command to force it to open or render
Thank you
MOAI doesn't run your scripts in a separate thread. A blocking call (server:accept) or forever loop (while true do) will block your MOAI app and it will appear to freeze while it merrily sits in your script forever.
So you have to do two things:
Use non-blocking calls. In this case, you need to set your server's timeout to 0. That makes server:accept return immediately. Check it's return value to see if you got a connection.
Put your while loop in a coroutine and yield once per iteration.
You'll need to handle the client the same way, using non-blocking calls in a coroutine loop.
function clientProc(client)
print('client connected:', client)
client:settimeout(0) -- make client socket reads non-blocking
while true do
local command, err = client:receive('*l')
if command then
print('received command:', command)
err = client:send("hi from Moai\n")
elseif err == 'closed' then
print('client disconnected:', client)
break
elseif err ~= 'timeout' then
print('error: ', err)
break
end
coroutine.yield()
end
client:close()
end
function serverProc()
print("server: waiting for client connections...")
server:settimeout(0) -- make server:accept call non-blocking
while true do
local client = server:accept()
if client then
MOAICoroutine.new():run(clientProc, client)
end
coroutine.yield()
end
end
MOAICoroutine.new():run(serverProc)
Set the timeout for the server socket, since the accept is a blocking call.
server:settimeout(1)
Thanks Mud...I found that before u replied so the following coroutine works
function threadFunc()
local action
while 1 do
stat, control = server:accept()
--print(control,stat)
while 1 do
if stat then
command, error = stat:receive()
print("Comm: ", command, error)
if command then
stat:close()
print("server: closing connection...")
break
else
break
end
--[[
error = stat:send("hi")
if error then
stat:close()
print("server: closing connection...",error)
break
end ]] --
else
break
end
end
coroutine.yield()
end
end
That was very helpful though