Commit 0bc979cf authored by Philipp Gast's avatar Philipp Gast
Browse files

This branch aims to reimplement MplServer based on python socketserver....

This branch aims to reimplement MplServer based on python socketserver. Additionally the objects are now serialized by pickle instead of our homebrew string representation. With this commit a remote call of readData is already working. If successful the classes Mplserver2 MplClient2 are drop in replacements for the unnumbered versions.
parent 52671e3c
......@@ -44,9 +44,9 @@ np.set_printoptions(threshold='nan')
if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
if D_HIERARCHY in ('CLIENT', 'client'):
print(INFO+'Import myplotlib as a client.')
print(INFO+'Import '+__package__+' as a client.')
else:
print(INFO+'Import myplotlib for local use.')
print(INFO+'Import '+__package__+' for local use.')
# Test mpl version:
from matplotlib import __version__ as mplvers
......@@ -69,7 +69,7 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
from socket import socket, AF_INET, SOCK_STREAM
elif D_HIERARCHY in ('SERVER', 'server'):
print(INFO+'Import myplotlib as a server.')
print(INFO+'Import '+__package__+' as a server.')
from .config import D_HOST, D_PORT
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
......@@ -91,9 +91,6 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
from .rconfig import rcParams
# BACKEND: u'TKAgg', u'GTKAgg', u'WXAgg', u'Qt4Agg', u'MacOSX'
rcParams['backend'] = u'GTKAgg'
if D_HIERARCHY in ('CLIENT', 'client'):
# MyAxes: Overlay on matplotlib.Axes class
......@@ -102,7 +99,10 @@ if D_HIERARCHY in ('CLIENT', 'client'):
# MyFig: Overlay on matplotlib.Figure class
from .myFig_client import MyFig_client as MyFig
from .progressbar import ProgressBar
from .mplClient import MplClient
from .mplClient2 import MplClient2
elif(D_HIERARCHY in ('SERVER', 'server')):
......@@ -116,12 +116,15 @@ elif(D_HIERARCHY in ('SERVER', 'server')):
from .test import FigTest
# for testing purpose
from .test import readStupidData
from .test import readStupidData, readStupidData2
SERVER_IOFUNCTIONS = {'readStupidData': readStupidData}
SERVER_IOFUNCTIONS = {'readStupidData': readStupidData,
'readStupidData2': readStupidData2
}
SERVER_FIGURES = {'FigTest': FigTest}
from .mplServer import MplServer
from .mplServer2 import MplServer2, MplHandler
elif D_HIERARCHY in ('LOCAL', 'local'):
......@@ -161,6 +164,8 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
from .mytool import print2file # need to make tools for server ?? or just put them in client/local
from .mytool import FigOneAxes
from .mytool import window_exists, getCurrentWindowIDs
from .mytool import print2file, print2screen, printListCurrentWindows
from .mytool import getWindow, getFigOnWindow, drawFigOnWindow, giveDataToWindow
......@@ -168,8 +173,6 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
# TESTS ----------------------------------------------------------------
if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'):
# from .test import myTest
# from .test import testList
from .test import FigTest
......
......@@ -37,7 +37,7 @@ rcParams['font.size'] = '22'
try:
rcParams['text.usetex'] = True # need dvipng, ghostscript, and Agg
except:
print(WARN + "I couls not set the LaTeX font sorry...")
print(WARN + "I could not set the LaTeX font sorry...")
# LINES
rcParams['lines.linewidth'] = 1.5
......
......@@ -2,7 +2,7 @@ from . import socket, AF_INET, SOCK_STREAM
from . import INFO, DBUG, WARN, SEVR, SPCE
SIZE_STATUS_MAX = 1024
from . import ProgressBar
from . import D_DEBUG
DEBUG = D_DEBUG
......@@ -254,7 +254,7 @@ class MplClient(object):
# Wait for the formatted data
packedData = self.waitForData(sock, dataSize)
print(INFO+str(packedData))
# print(INFO+str(packedData))
# Unpack
header, datas, errmsg = self.unpackFormattedData(packedData)
......@@ -298,9 +298,12 @@ class MplClient(object):
'''Wait for data of size dataSize.'''
receivedSize = 0
unpackedData = b''
progress = ProgressBar(dataSize, fmt=ProgressBar.FULL)
while receivedSize < dataSize:
unpackedData += sock.recv(4096)
progress(4096)
receivedSize = len(unpackedData)
progress.done()
return(unpackedData)
# UNPACKING --------------------------------------------------------
......@@ -317,7 +320,7 @@ class MplClient(object):
header, content, errmsg = eval(packedData)
except:
print(WARN+'The data sent by the server could not be evaluated')
print(SPCE+str(packedData))
# print(SPCE+str(packedData))
return((None, None, None))
# The tuple of as many strings as axes
......
from . import socket, AF_INET, SOCK_STREAM
import pickle
from .signal import Signal,Query,Status,Answer
debug = True
class MplClient2():
def __init__(self,con):
self.host,self.port = con # give (IP,PORT)
self.currentRemoteData = []
self.sock = socket(AF_INET,SOCK_STREAM)
self.sock.connect(con)
# just a list of names
self.currentRemoteData = []
#~ def reset_connection(self,new_con=(self.host,self.port)):
#~ # shutdown
#~ self.sock.shutdown()
#~ self.sock.close()
#~ # start-up
#~ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#~ self.sock.connect(con)
def send(self,sig):
# pickel the signal
# send the str
wf = self.sock.makefile(mode='wb')
if debug:
print('sending ',type(sig),' with content \"',sig.content,'\"')
#~ try :
print(sig.__class__)
pickle.dump(sig,wf)
#~ except :
#~ return False
wf.close()
return True
def waitForSignal(self):
# wait for signal
# unpickel
rf = self.sock.makefile(mode='rb')
try :
print('before pickle')
response = pickle.load(rf)
print('after pickle')
if debug:
print('CLIENT Received: {} with content \"{}\"'.format(type(response),response.content))
except EOFError :
print('no answer received')
response = None
#~ except :
#~ return False
rf.close()
if isinstance(response,Signal) :
return response
def testStatusSig(self,orgQuery,statusSig) :
if isinstance(statusSig,Status) :
if statusSig.value :
if debug :
print ('request type ',orgQuery.queryType,' successful' )
return True
else :
if debug :
print('something went wrong on the server side handling request type',orgQuery.queryType)
print(statusSig.content)
return False
else :
if debug :
print('request type ',orgQuery.queryType,' retured unknown status type with content: \"',statusSig.content,'\"')
return False
def readData(self,ioFunction, dataName, *args, **kwargs) :
# create a Signal of type Query with arguments
query = Query(Query.READDATA,{'func' : ioFunction, 'dataname' : dataName,'args' : args,'kwargs': kwargs})
status = self.send(query)
print(status)
if not status :
print('something went wrong with the sending of readData request...')
statusSig = self.waitForSignal()
print(statusSig)
return self.testStatusSig(query,statusSig)
#~ def newSyncFigure() :
#~ '''
#~ Send a query NEW_SYNC_FIGURE to the server
#~ wait for the status.
#~ if status ok then sync.
#~ '''
#~ # create a Signal of type Query with args
#~ query = Query(Query.NEWSYNCFIGURE)
#~ status = send(query)
#~ if not status :
#~ print('something went wrong with the sending of newSincFigure request...')
#~ statusSig = waitForSignal()
#~
#~ if statusSig.value :
#~ # create Figure with args
#~ if created
#~ # create a signal of type status (True)
#~ status = Status(True)
#~ else:
#~ # create a signal of type status (False)
#~ status = Status(False)
#~ sendSignal(status)
#~ else:
#~ # do not create the figure.
#~ print statusSig.error
#~ def updateSyncFigure()
#~ '''
#~ Send a UPDATE_SYNC_FIGURE query to the server.
#~ wait for the status
#~ if status ok then sync.
#~ '''
#~ # create a signal with args
#~ query = Query(Query.UPDATE_SYNC_FIGURE)
#~ sendSignal(query)
#~ statusSig = waitForSignal()
#~
#~ if statusSig.value :
#~ # update Figure with args
#~ if updated
#~ # create a signal of type status (True)
#~ status = Status(True)
#~ else:
#~ # create a signal of type status (False)
#~ status = Status(False)
#~ sendSignal(status)
#~ else:
#~ # do not update the figure.
#~ print statusSig.error
#~
#~ def deleteSyncFigure()
#~ '''
#~ Send a DELETE_SYNC_FIGURE query to the server.
#~ wait for the status
#~ if status ok then sync.
#~ '''
#~ # create a signal with args
#~ query = Query(Query.DELETE_SYNC_FIGURE)
#~ sendSignal(query)
#~ statusSig = waitForSignal()
#~ if statusSig.value:
#~ # delete local figure.
#~ else:
#~ print statusSig.error
#~
#~ def formatSyncFigure()
#~ '''
#~ Send a FORMAT_SYNC_FIGURE query to the server.
#~ wait for the answer
#~ if answer not empty then set data to answer's value.
#~ '''
#~ # create signal with args
#~ query = Query(Query.FORMAT_SYNC_FIGURE)
#~ sendSignal(query)
#~ answerSig = waitForSIgnal()
#~ if answerSig.value is not None:
#~ # set the figure data to answerSig.value
#~ else:
#~ # print answerSig.error
#~
import SocketServer
import pickle
from .signal import Signal,Status,Query,Answer
from . import SERVER_IOFUNCTIONS
from . import SERVER_FIGURES
from . import DBUG,WARN,SPCE,SEVR,INFO
G_RAWDATA = dict()
DEBUG = True
class MplHandler(SocketServer.StreamRequestHandler):
def servPrint(self,x) :
print("SERVER: "+x)
def treatReadData(self,content):
'''Treat signals of type readData.
1. unpack signal with instruction size,
2. send receipt,
3. unpack instructions,
4. execute instructions,
5. send dataID.
'''
fct = SERVER_IOFUNCTIONS[content.get('func')]
args = content.get('args')
kwargs = content.get('kwargs')
dataName = content.get('dataname')
# Try to execute the instructions
try:
G_RAWDATA[dataName] = fct(*args, **kwargs)
if (DEBUG):
print(INFO+"SERVER: I read the data following instructions")
# print(str(G_RAWDATA[dataName].data)) # SHOULD BE SUPPRESS LATER
except:
if(DEBUG):
print(WARN+"SERVER: The instructions of readData could not be executed.")
print(SPCE+"SERVER: I tried: "+str(fct.__name__)+'('+str(args)+', '+str(kwargs)+')')
status = Status(False,"The instructions of readData could not be executed. I tried: "+str(fct.__name__)+'('+str(args)+', '+str(kwargs)+')')
if dataName in G_RAWDATA :
status = Status(True,"SERVER: I read the data following instructions"+str(fct.__name__)+'('+str(args)+', '+str(kwargs)+')')
return status
def treatNewSyncFigure(content) :
pass
def treatUpdateSyncFigure(content) :
pass
def treatSyncFigFormatRawData(content):
pass
def treatSig(self,sig):
print('I am here')
print(str(sig))
print(type(sig))
print(isinstance(sig, Query))
if isinstance(sig,Query) :
if sig.queryType == sig.READDATA :
return self.treatReadData(sig.content)
elif sig.queryType == sig.NEWSYNCFIGURE :
return self.treatNewSyncFigure(sig.content)
elif sig.queryType == sig.UPDATESYNCFIGURE :
return self.treatUpdateSyncFigure(sig.content)
elif sig.queryType == sig.SYNCFIGFORMATRAWDATA :
return self.treatSyncFigFormatRawData(sig.content)
else :
self.servPrint('received unknown query type')
return Status(False,"received unknown query type")
elif isinstance(sig, Status) :
if sig.value :
pass #if it is just a positiv status message do nothing
else :
self.servPrint('the client Side reported of an error:')
self.servPrint('----'+sig.content)
elif isinstance(sig, Answer) :
#~ #needed for server???
pass
elif isinstance(sig, Signal) :
self.servPrint('received unknown signal')
return Status(False,"received unknown signal")
else :
self.servPrint('what is happening here??? received Object of type:')
self.servPrint(str(sig))
return Status(False,"received something totally diffenent")
def handle(self):
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
## self.data = self.rfile.readline()#.strip()
print('before pickle')
request = pickle.load(self.rfile)
print('after pickle')
reply = self.treatSig(request)
# Likewise, self.wfile is a file-like object used to write back
# to the client
## self.wfile.write(self.data.upper())
if isinstance(reply,Signal) :
self.servPrint("returning {}-Signal:".format(type(reply)))
pickle.dump(reply,self.wfile)
class MplServer2(SocketServer.TCPServer):
allow_reuse_address=True
# DEFINE KNOWN I/O FUNCTIONS ---------------------------------------
def defineKnownFunctions(self):
'''Function to overwrite in order to declare the known
I/O functions by the server context.'''
self.knownFunctions = SERVER_IOFUNCTIONS
# DEFINE KNOWN FIGURE CLASSES --------------------------------------
def defineKnownFigures(self):
'''Function to overwrite in order to declare the known
Figure classes by the server context.'''
self.knownFigures = SERVER_FIGURES
def __init__(self,*args, **kwargs) :
'''Intialisation of the server, defines known functions, known
figure classes. It needs to be called.'''
# Define functions and figure classes
self.defineKnownFunctions()
self.defineKnownFigures()
SocketServer.TCPServer.__init__(self,*args, **kwargs)
self.initialized = True
from __future__ import print_function
import sys
class ProgressBar(object):
DEFAULT = "{bar} {current}/{total} ({percent}%) {remaining} to go"
FULL = "{bar} {current}/{total} kB ({percent:3.1f}%) {remaining:>10} kB to go"
def __init__(self, total, width=40, fmt=DEFAULT, symbol='=',
output=sys.stderr):
assert len(symbol) == 1
self.total = total
self.width = width
self.symbol = symbol
self.output = output
if total > 1024:
unit = 'kB'
self.convert = 1024
else:
unit = 'Bit'
self.convert = 1
if total > 1048576:
unit = 'MB'
self.convert = 1048576
if total > 1073741824:
unit = 'GB'
self.convert = 1073741824
lg = len(str(total/self.convert))
self.fmt = "{bar} {current}/{total} "+str(unit)+" ({percent:3.1f}%) {remaining:>"+str(lg)+"} "+str(unit)+" to go"
self.current = 0
def __call__(self,inc):
percent = self.current / float(self.total)
size = int(self.width * percent)
remaining = self.total - self.current
bar = '[' + self.symbol * size + ' ' * (self.width - size) + ']'
args = {
'total': self.total / self.convert,
'bar': bar,
'current': self.current / self.convert,
'percent': percent * 100,
'remaining': remaining / self.convert
}
print('\r' + self.fmt.format(**args), file=self.output, end='')
self.current += inc
def done(self):
self.current = self.total
self(0)
print('', file=self.output)
from matplotlib import rcParams
# BACKEND: u'TKAgg', u'GTKAgg', u'WXAgg', u'Qt4Agg', u'MacOSX'
rcParams['backend'] = u'TKAgg'
# FONT
rcParams['font.family'] = 'serif'
rcParams['font.size'] = '22'
......
class Signal (object):
def __init__(self,content) :
# every signal should have a content attribute
self.content = content
class Query(Signal) :
READDATA = 1
NEWSYNCFIGURE = 2
UPDATESYNCFIGURE = 3
SYNCFIGFORMATRAWDATA = 4
def __init__(self,queryType, content) :
if queryType in [Query.READDATA,Query.NEWSYNCFIGURE,Query.UPDATESYNCFIGURE,Query.SYNCFIGFORMATRAWDATA] :
self.queryType = queryType
if queryType == Query.READDATA :
if type(content) == dict :
self.content = content
else :
print('parameter 2 of Query constructor is exspected to be of type dict')
raise
elif queryType == Query.NEWSYNCFIGURE :
if type(content) == dict : # same as
self.content = content
else :
print('parameter 2 of Query constructor is exspected to be of type dict')
raise
elif queryType == Query.UPDATESYNCFIGURE :
pass
elif queryType == Query.SYNCFIGFORMATRAWDATA :
pass
class Status(Signal):
def __init__(self,value,error) :
if (value == True) or (value == False) :
self.value = value
if (type(error) == str) :
self.content = error
class Answer(Signal):
def __init__(self,value) :
self.content = value
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment