Skip to content

Commit

Permalink
Merge pull request #2 from kendas/py3
Browse files Browse the repository at this point in the history
Python 3 compatibility
  • Loading branch information
raidoz authored Jun 5, 2019
2 parents 3396227 + 79d286a commit c30fc94
Show file tree
Hide file tree
Showing 20 changed files with 472 additions and 112 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
*.pyc
__pycache__
.idea
.vscode
dist
moteconnection.egg-info
*.egg-info
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: python
python:
# Python 2
- "2.7"
# Python 3
- "3.5"
- "3.6"
- "3.7-dev"
# - "3.7"
# - "3.8-dev"
install:
- pip install -r requirements.txt
script:
- nosetests
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Raido Pahtma <[email protected]>
Kaarel Ratas <[email protected]>
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# python-moteconnection

Python library for using TinyOS inspired serial and tcp connections.
Empty file added examples/__init__.py
Empty file.
95 changes: 95 additions & 0 deletions examples/sniffer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
This example sets up a connection and listens to all incoming packets.
To run this example as a script, the following command:
```
$ python -m example.sniffer sf@location:port
```
"""
from __future__ import print_function

from argparse import ArgumentParser
from functools import partial
import logging
import time

from moteconnection.connection import Connection
from moteconnection.message import MessageDispatcher


def start_listen(connection_string):
"""Begins listening for incoming packets."""

connection = construct_connection(connection_string)
while 1:
try:
time.sleep(100)
except (KeyboardInterrupt, SystemExit) as e:
print("Received {!r}".format(e))
print("Shutting down")
connection.disconnect()
connection.join()
break

connection.disconnect()
connection.join()


def construct_connection(connection_string):
"""
Constructs the connection object and returns it.
The connection string takes the form of protocol@location:port_or_baud
Examples: sf@localhost:9002
serial@/dev/ttyUSB0
:param str connection string: A string in the form of protocol@location:port_or_baud
:rtype: moteconnection.connection.Connection
"""
connection = Connection()
connection.connect(
connection_string,
reconnect=10,
connected=partial(print, "Connected to {}".format(connection_string)),
disconnected=partial(print, "Disconnected from {}".format(connection_string))
)

dispatcher = MessageDispatcher()
# This example uses a callback function (print in this case). The callback function
# _must_ take exactly 1 positional argument. That argument will be an instance of
# `moteconnection.message.Message`.
# The alternatice method to using a callback function is to pass an instance of
# `queue.Queue` (python3) or `Queue.Queue` (python2) to these methoods.
dispatcher.register_default_snooper(print)
dispatcher.register_default_receiver(print)
connection.register_dispatcher(dispatcher)
return connection


def get_args():
"""
Parses the arguments and returns them.
:rtype argparse.Namespace
"""
parser = ArgumentParser(description='An example moteconnection listening program.')
parser.add_argument('connection',
help="The connection string used to connect to the device. "
"Can be a serial forwarder address or a serial device address.")
parser.add_argument('--verbose', '-v',
action='store_true',
default=False,
help='Verbose mode (displays moteconnection logs).')
return parser.parse_args()


def main():
"""Main entrypoint to the sniffer application."""
args = get_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
start_listen(connection_string=args.connection)


if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion moteconnection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
__license__ = "MIT"


version = '0.1.7'
version = '0.1.8'
27 changes: 15 additions & 12 deletions moteconnection/connection.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"""connection.py: Connection for connecting to serial or sf ports."""

import time
import Queue
import logging
import threading
import time
from codecs import encode

from six.moves import queue

from moteconnection.utils import split_in_two
from moteconnection.connection_events import ConnectionEvents
from moteconnection.connection_serial import SerialConnection
from moteconnection.connection_forwarder import SfConnection
from moteconnection.connection_serial import SerialConnection
from moteconnection.utils import split_in_two

import logging
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)

Expand Down Expand Up @@ -50,7 +52,7 @@ def __init__(self, autostart=True):
# New connection types can be added here
self.connection_types = {"loopback": LoopbackConnection, "sf": SfConnection, "serial": SerialConnection}

self._queue = Queue.Queue()
self._queue = queue.Queue()

# Can be connected, disconnected or somewhere in between
self._connected = threading.Event()
Expand Down Expand Up @@ -96,6 +98,7 @@ def connected(self):

def connect(self, connection_string, reconnect=None, connected=None, disconnected=None):
"""
:param connection_string:
:param reconnect: Optional reconnect period. Connection is attempted once if not set.
:param connected: Optional callback for receiving connection establishment notifications.
:param disconnected: Optional callback for receiving disconnection notifications.
Expand Down Expand Up @@ -141,11 +144,11 @@ def _subsend(self, packet):

def _receive(self, data):
if len(data) > 0:
dispatch = ord(data[0])
dispatch = ord(data[0:1])
if dispatch in self._dispatchers:
self._dispatchers[dispatch].receive(data)
else:
log.debug("No dispatcher for receiving {:02X}".format(dispatch))
log.debug("No dispatcher for receiving %02X", dispatch)
else:
log.debug("Received 0 bytes of data ...")

Expand All @@ -154,10 +157,10 @@ def run(self):
try:
item_type, item = self._queue.get(True, 1.0)
if item_type == ConnectionEvents.MESSAGE_INCOMING:
log.debug("incoming {:s}".format(item.encode("hex")))
log.debug("incoming %s", encode(item, "hex"))
self._receive(item)
elif item_type == ConnectionEvents.MESSAGE_OUTGOING:
log.debug("outgoing {:s}".format(item))
log.debug("outgoing %s", item)
self._real_connection.send(item)
elif item_type == ConnectionEvents.EVENT_CONNECTED:
log.info("connected")
Expand All @@ -175,7 +178,7 @@ def run(self):
self._connect()
else:
raise Exception("item_type is unknown!")
except Queue.Empty:
except queue.Empty:
if self._disconnected.isSet():
if self._reconnect_period is not None and self._reconnect_period >= 0:
if time.time() > self._last_connect + self._reconnect_period:
Expand All @@ -199,7 +202,7 @@ def dispatch(self):

@staticmethod
def _deliver(receiver, message):
if isinstance(receiver, Queue.Queue):
if isinstance(receiver, queue.Queue):
receiver.put(message)
else:
receiver(message)
Expand Down
4 changes: 3 additions & 1 deletion moteconnection/connection_events.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""connection_events.py: Connection event types."""

from enum import Enum


__author__ = "Raido Pahtma"
__license__ = "MIT"


class ConnectionEvents(object):
class ConnectionEvents(Enum):
MESSAGE_INCOMING = 0
MESSAGE_OUTGOING = 1
EVENT_START_CONNECT = 3
Expand Down
38 changes: 20 additions & 18 deletions moteconnection/connection_forwarder.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
""""connection_forwarder.py: SF connection object."""

import logging
import socket

import threading
from StringIO import StringIO
from moteconnection.utils import split_in_two
from codecs import encode

from six import BytesIO

from moteconnection.connection_events import ConnectionEvents
from moteconnection.utils import split_in_two

import logging
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)

Expand All @@ -18,7 +20,7 @@

class SfConnection(threading.Thread):

PROTOCOL_VERSION = "U "
PROTOCOL_VERSION = b"U "

def __init__(self, event_queue, host_and_port):
super(SfConnection, self).__init__()
Expand Down Expand Up @@ -50,14 +52,14 @@ def send(self, packet):
acked = False
if self._connected.isSet():
try:
self._socket.sendall(chr(len(data)))
self._socket.sendall(chr(len(data)).encode())
self._socket.sendall(data)
acked = True
log.debug("snt {:s}".format(data.encode("hex")))
log.debug("snt %s", encode(data, "hex"))
except socket.error:
self._disconnected()
else:
log.debug("drop {:s}".format(data.encode("hex")))
log.debug("drop %s", encode(data, "hex"))

if packet.callback:
packet.callback(packet, acked)
Expand All @@ -81,36 +83,36 @@ def _connect(self):
self._socket.sendall(self.PROTOCOL_VERSION)
log.debug("handshake sent")

buf = StringIO()
while buf.len < 2:
data = self._socket.recv(2 - buf.len)
buf = BytesIO()
while len(buf.getvalue()) < 2:
data = self._socket.recv(2 - len(buf.getvalue()))
if data:
buf.write(data)
else:
raise socket.error("no data received")

if buf.getvalue() == "U ":
if buf.getvalue() == b"U ":
log.debug("handshake success")
self._connected.set()
self._queue.put((ConnectionEvents.EVENT_CONNECTED, None))
else:
raise socket.error("handshake mismatch '{:s}' != '{:s}'".format(self.PROTOCOL_VERSION, buf.getvalue()))
raise socket.error("handshake mismatch {!s} != {!s}".format(self.PROTOCOL_VERSION, buf.getvalue()))

def _receive(self):
try:
if self._recv_length == 0:
length = self._socket.recv(1)
if length:
self._recv_length = ord(length)
self._recv_buf = StringIO()
self._recv_buf = BytesIO()
else:
raise socket.error("no data received")
else:
data = self._socket.recv(self._recv_length - self._recv_buf.len)
data = self._socket.recv(self._recv_length - len(self._recv_buf.getvalue()))
if data:
self._recv_buf.write(data)
if self._recv_length == self._recv_buf.len:
log.debug("rcv {:s}".format(self._recv_buf.getvalue().encode("hex")))
if self._recv_length == len(self._recv_buf.getvalue()):
log.debug("rcv %s", encode(self._recv_buf.getvalue(), "hex"))
self._recv_length = 0
return self._recv_buf.getvalue()
else:
Expand All @@ -130,6 +132,6 @@ def run(self):
if data is not None:
self._queue.put((ConnectionEvents.MESSAGE_INCOMING, data))
except socket.error as e:
log.error("socket.error: {:s}".format(e.message))
log.error("socket.error: %s", e.args)
finally:
self._disconnected()
Loading

0 comments on commit c30fc94

Please sign in to comment.