stringio now works with serial connections
also allow SECoP client connections via serial Change-Id: I10c02532a9f8e9b8f16599b98c439742da6d8f5c Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/22525 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
@@ -24,9 +24,9 @@ implements TCP/IP and is be used as a base for SerialIO
|
||||
"""
|
||||
|
||||
import time
|
||||
import socket
|
||||
import threading
|
||||
import re
|
||||
from secop.lib.asynconn import AsynConn, ConnectionClosed
|
||||
from secop.modules import Module, Communicator, Parameter, Command, Property, Attached
|
||||
from secop.datatypes import StringType, FloatRange, ArrayOf, BoolType, TupleOf
|
||||
from secop.errors import CommunicationFailedError, CommunicationSilentError
|
||||
@@ -34,14 +34,9 @@ from secop.poller import REGULAR
|
||||
from secop.metaclass import Done
|
||||
|
||||
|
||||
|
||||
class StringIO(Communicator):
|
||||
"""line oriented communicator
|
||||
|
||||
implementation for TCP/IP streams.
|
||||
other types have to override the following methods:
|
||||
createConnection, readWithTimeout, writeBytes, closeConnection
|
||||
|
||||
self healing is assured by polling the parameter 'is_connected'
|
||||
"""
|
||||
properties = {
|
||||
@@ -76,82 +71,35 @@ class StringIO(Communicator):
|
||||
_reconnectCallbacks = None
|
||||
|
||||
def earlyInit(self):
|
||||
self._stream = None
|
||||
self._conn = None
|
||||
self._lock = threading.RLock()
|
||||
self._end_of_line = self.end_of_line.encode(self.encoding)
|
||||
self._connect_error = None
|
||||
self._last_error = None
|
||||
|
||||
def createConnection(self):
|
||||
"""create connection
|
||||
|
||||
in case of success, self.is_connected MUST be set to True by implementors
|
||||
"""
|
||||
uri = self.uri
|
||||
if uri.startswith('tcp://'):
|
||||
uri = uri[6:]
|
||||
try:
|
||||
host, port = uri.split(':')
|
||||
self._stream = socket.create_connection((host, int(port)), 10)
|
||||
self.is_connected = True
|
||||
except (ConnectionRefusedError, socket.gaierror) as e:
|
||||
raise CommunicationFailedError(str(e))
|
||||
except Exception as e:
|
||||
# this is really bad, do not try again
|
||||
self._connect_error = e
|
||||
raise
|
||||
|
||||
def readWithTimeout(self, timeout):
|
||||
"""read with timeout
|
||||
|
||||
Read bytes available now, or wait at most the specified timeout until some bytes
|
||||
are available. Throw an error, if disconnected.
|
||||
If no bytes are available, return b''
|
||||
|
||||
to be overwritten for other stream types
|
||||
"""
|
||||
if timeout is None or timeout < 0:
|
||||
raise ValueError('illegal timeout %r' % timeout)
|
||||
def connectStart(self):
|
||||
if not self.is_connected:
|
||||
raise CommunicationSilentError(self._last_error or 'not connected')
|
||||
self._stream.settimeout(timeout)
|
||||
try:
|
||||
reply = self._stream.recv(4096)
|
||||
if reply:
|
||||
return reply
|
||||
except (BlockingIOError, socket.timeout):
|
||||
return b''
|
||||
except Exception as e:
|
||||
self.closeConnection()
|
||||
raise CommunicationFailedError('disconnected because of %s' % e)
|
||||
# other end disconnected
|
||||
self.closeConnection()
|
||||
raise CommunicationFailedError('other end disconnected')
|
||||
|
||||
def writeBytes(self, data):
|
||||
"""write bytes
|
||||
|
||||
to be overwritten for other stream types
|
||||
"""
|
||||
self._stream.sendall(data)
|
||||
|
||||
uri = self.uri
|
||||
try:
|
||||
self._conn = AsynConn(uri, self._end_of_line)
|
||||
self.is_connected = True
|
||||
except Exception as e:
|
||||
# this is really bad, do not try again
|
||||
self._connect_error = e
|
||||
raise
|
||||
for command, regexp in self.identification:
|
||||
reply = self.do_communicate(command)
|
||||
if not re.match(regexp, reply):
|
||||
self.closeConnection()
|
||||
raise CommunicationFailedError('bad response: %s does not match %s' %
|
||||
(reply, regexp))
|
||||
def closeConnection(self):
|
||||
"""close connection
|
||||
|
||||
self.is_connected MUST be set to False by implementors
|
||||
"""
|
||||
if not self._stream:
|
||||
return
|
||||
self.log.debug('disconnect %s' % self.uri)
|
||||
try:
|
||||
self._stream.shutdown(socket.SHUT_RDWR)
|
||||
except socket.error:
|
||||
pass
|
||||
try:
|
||||
self._stream.close()
|
||||
except socket.error:
|
||||
pass
|
||||
self._stream = None
|
||||
self._conn.disconnect()
|
||||
self._conn = None
|
||||
self.is_connected = False
|
||||
|
||||
def read_is_connected(self):
|
||||
@@ -185,16 +133,6 @@ class StringIO(Communicator):
|
||||
return False
|
||||
return self.read_is_connected()
|
||||
|
||||
def connectStart(self):
|
||||
if not self.is_connected:
|
||||
self.createConnection()
|
||||
for command, regexp in self.identification:
|
||||
reply = self.do_communicate(command)
|
||||
if not re.match(regexp, reply):
|
||||
self.closeConnection()
|
||||
raise CommunicationFailedError('bad response: %s does not match %s' %
|
||||
(reply, regexp))
|
||||
|
||||
def registerReconnectCallback(self, name, func):
|
||||
"""register reconnect callback
|
||||
|
||||
@@ -236,25 +174,15 @@ class StringIO(Communicator):
|
||||
if self.wait_before:
|
||||
time.sleep(self.wait_before)
|
||||
if garbage is None: # read garbage only once
|
||||
garbage = b''
|
||||
data = self.readWithTimeout(0)
|
||||
while data:
|
||||
garbage += data
|
||||
data = self.readWithTimeout(0)
|
||||
garbage = self._conn.flush_recv()
|
||||
if garbage:
|
||||
self.log.debug('garbage: %s', garbage.decode(self.encoding))
|
||||
self.writeBytes((cmd + self.end_of_line).encode(self.encoding))
|
||||
timeout = self.timeout
|
||||
buffer = b''
|
||||
data = True
|
||||
while data:
|
||||
data = self.readWithTimeout(timeout)
|
||||
buffer += data
|
||||
if self._end_of_line in buffer:
|
||||
break
|
||||
else:
|
||||
raise CommunicationFailedError('timeout')
|
||||
reply = buffer.split(self._end_of_line, 1)[0].decode(self.encoding)
|
||||
self._conn.send((cmd + self.end_of_line).encode(self.encoding))
|
||||
try:
|
||||
reply = self._conn.readline(self.timeout)
|
||||
except ConnectionClosed:
|
||||
raise CommunicationFailedError('disconnected')
|
||||
reply = reply.decode(self.encoding)
|
||||
self.log.debug('recv: %s', reply)
|
||||
return reply
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user