COM-Server library API reference
com_server.Connection
BaseConnection
Below are members of Connection inherited from BaseConnection.
A base connection object with a serial or COM port.
Base class that contains implemented basic methods: send(), receive(), connect(),
and disconnect(), properties of the connection, and an abstract IO thread method.
available: int
property
readonly
A property indicating how much new data there is in the receive queue.
Getter:
- Gets the number of additional data received since the user last called the
receive()method.
conn_obj: Serial
property
readonly
A property to get the Serial object that handles sending and receiving.
Getter:
- Gets the Serial object.
connected: bool
property
readonly
A property to determine if the connection object is currently connected to a serial port or not.
This also can determine if the IO thread for this object is currently running or not.
port: str
property
readonly
Returns the current port of the connection
Getter:
- Gets the current port of the connection
send_interval: float
property
writable
A property to determine the send interval of this object.
Getter:
- Gets the send interval of this object.
Setter:
- Sets the send interval of this object after checking if convertible to nonnegative float.
timeout: float
property
writable
A property to determine the timeout of this object.
Getter:
- Gets the timeout of this object.
Setter:
- Sets the timeout of this object after checking if convertible to nonnegative float.
Then, sets the timeout to the same value on the
pyserialobject of this class. If the value isfloat('inf'), then sets the value of thepyserialobject to None.
__init__(self, baud, port, *ports, *, exception=True, timeout=1, send_interval=1, queue_size=256, exit_on_disconnect=False, rest_cpu=True, **kwargs)
special
Initializes BaseConnection and Connection-like classes
baud, port (or a port within ports), timeout, and kwargs will be passed to pyserial.
For more information, see here.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
connect(self)
Begins connection to the serial port.
When called, initializes a serial instance if not initialized already. Also starts the IO thread.
| Exceptions: |
|
|---|
disconnect(self)
Closes connection to the serial port.
When called, calls Serial.close() then makes the connection None.
If it is currently closed then just returns.
Forces the IO thread to close.
NOTE: This method should be called if the object will not be used anymore or before the object goes out of scope, as deleting the object without calling this will lead to stray threads.
receive(self, num_before=0)
Returns the most recent receive object.
The IO thread will continuously detect data from the serial port and put the bytes objects in the rcv_queue.
If there are no parameters, the method will return the most recent received data.
If num_before is greater than 0, then will return num_beforeth previous data.
- Note: num_before must be less than the current size of the queue and greater or equal to 0
- If not, returns None (no data)
- Example:
- 0 will return the most recent received data
- 1 will return the 2nd most recent received data
- ...
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
send(self, *data, *, check_type=True, ending='\r\n', concatenate=' ')
Sends data to the port
If the connection is open and the interval between sending is large enough,
then concatenates args with a space (or what was given in concatenate) in between them,
encodes to an utf-8 bytes object, adds a carriage return and a newline to the end
(i.e. "\r\n") (or what was given as ending), then sends to the serial port.
Note that the data does not send immediately and instead will be added to a queue.
The queue size limit is 65536 byte objects. Calling send() more than this limit will not add objects to the queue.
Sending data too rapidly (e.g. making send_interval too small, varies from computer to computer) is not recommended,
as the queue will get too large and the send data will get backed up be delayed,
because it takes a considerable amount of time for data to be sent through the serial port.
Additionally, parts of the send queue will be all sent together until it reaches 0.5 seconds,
which may end up with unexpected behavior in some programs.
To prevent these problems, either make the value of send_interval larger,
or add a delay within the main thread.
If the program has not waited long enough before sending, then the method will return false.
If check_type is True, then it will process each argument, then concatenate, encode, and send.
- If the argument is bytes then decodes to str
- If argument is list or dict then passes through json.dumps
- If argument is set or tuple then converts to list and passes through json.dumps
- Otherwise, directly convert to str and strip
Otherwise, converts each argument directly to str and then concatenates, encodes, and sends.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
Connection
Below are members of Connection not inherited from BaseConnection.
Class that interfaces with the serial port.
Warning: Before making this object go out of scope, make sure to call disconnect() in order to avoid zombie threads.
If this does not happen, then the IO thread will still be running for an object that has already been deleted.
all_rcv(self, return_bytes=False, read_until=None, strip=True)
Returns entire receive queue
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
conv_bytes_to_str(self, rcv, read_until=None, strip=True)
Converts bytes object to string given parameters
| Parameters: |
|
|---|
| Returns: |
|
|---|
custom_io_thread(self, func)
A decorator custom IO thread rather than using the default one.
It is recommended to read pyserial's documentation before creating a custom IO thread.
What the IO thread executes every 0.01 seconds will be referred to as a "cycle".
Note that this method should be called before connect() is called, or
else the thread will use the default cycle.
To see the default cycle, see the documentation of BaseConnection.
What the IO thread will do now is:
- Check if anything is using (reading from/writing to) the variables
- If not, copy the variables into a
SendQueueandReceiveQueueobject. - Call the
custom_io_threadfunction (if none, calls the default cycle) - Copy the results from the function back into the send queue and receive queue.
- Rest for 0.01 seconds to rest the CPU
The cycle should be in a function that this decorator will be on top of. The function should accept three parameters:
conn(aserial.Serialobject)rcv_queue(aReceiveQueueobject; see more on how to use it in its documentation)send_queue(aSendQueueobject; see more on how to use it in its documentation)
To enable autocompletion on your text editor, you can add type hinting:
from com_server import Connection, SendQueue, ReceiveQueue
from serial import Serial
conn = Connection(...)
# some code
@conn.custom_io_thread
def custom_cycle(conn: Serial, rcv_queue: ReceiveQueue, send_queue: SendQueue):
# code here
conn.connect() # call this AFTER custom_io_thread()
# more code
The function below the decorator should not return anything.
get(self, return_bytes=False, read_until=None, strip=True)
Gets first response after this method is called.
This method waits for an object to be received from the serial port and returns that object. If the timeout is reached while waiting, then this method will return None.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
get_first_response(self, *data, *, return_bytes=False, ending='\r\n', concatenate=' ', read_until=None, strip=True)
Gets the first response from the serial port after sending something.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
receive_str(self, num_before=0, read_until=None, strip=True)
Returns the most recently received object as a processed string.
To get the bytes object, use Connection.receive().
The IO thread will continuously detect data from the serial port and put the bytes objects in the rcv_queue.
If there are no parameters, the method will return the most recent received data.
If num_before is greater than 0, then will return num_beforeth previous data.
- Note: Must be less than the current size of the queue and greater or equal to 0
- If not, returns None (no data)
- Example:
- 0 will return the most recent received data
- 1 will return the 2nd most recent received data
- ...
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
reconnect(self, timeout=None)
Attempts to reconnect the serial port.
This method will continuously try to connect to the ports provided in __init__()
until it reaches given timeout seconds. If timeout is None, then it will
continuously try to reconnect indefinitely.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
send_for_response(self, response, *data, *, read_until=None, strip=True, ending='\r\n', concatenate=' ')
Sends something until the connection receives a given response or timeout is reached.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
wait_for_response(self, response, after_timestamp=-1.0, read_until=None, strip=True)
Waits until the connection receives a given response.
This method will wait for a response that matches given response
whose time received is greater than given timestamp after_timestamp.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
| Returns: |
|
|---|
com_server.ConnectionRoutes
A wrapper for Flask objects for adding routes involving a Connection object
This class allows the user to easily add REST API routes that interact
with a serial connection by using flask_restful.
When the connection is disconnected, a 500 Internal Server Error
will occur when a route relating to the connection is visited.
A thread will detect this event and will try to reconnect the serial port.
Note that this will cause the send and receive queues to reset.
If a resource is accessed while it is being used by another process,
then it will respond with 503 Service Unavailable.
More information on Flask and flask-restful.
__init__(self, conn)
special
Constructor
There should only be one ConnectionRoutes object that wraps each Connection object.
Having multiple may result in an error.
Note that conn needs to be connected when starting
the server or else an error will be raised.
| Parameters: |
|
|---|
add_resource(self, resource)
Decorator that adds a resource
The resource should interact with the serial port.
If not, use Api.add_resource() instead.
This decorator works the same as Api.resource().
However, the class under the decorator should
not extend flask_restful.Resource but
instead com_server.ConnectionResource. This is
because ConnectionResource contains Connection
attributes that can be used in the resource.
Unlike a resource added using Api.add_resource(),
if a process accesses this resource while it is
currently being used by another process, then it will
respond with 503 Service Unavailable.
Currently, supported methods are:
GETPOSTPUTPATCHDELETEOPTIONSHEAD
Make sure to put method names in lowercase
| Parameters: |
|
|---|
com_server.start_app
com_server.server.start_app(app, api, *routes, *, logfile=None, host='0.0.0.0', port=8080, cleanup=None, **kwargs)
Starts a waitress production server that serves the app
Note that connection objects between ConnectionRoutes
can share no ports in common.
Using this is recommended over calling add_resources(),
start_conns(), serve_app(), and disconnect_conns()
separately.
Also note that adding multiple ConnectionRoutes is
not tested and may result in very unexpected behavior
when disconnecting and reconnecting.
Lastly, note that sys.exit() will be called in this,
so add any cleanup operations to the cleanup parameter.
| Parameters: |
|
|---|
com_server.add_resources
com_server.server.add_resources(api, *routes)
Adds all resources given in servers to the given Api.
This has to be called along with start_conns() before calling start_app() or running a flask app.
| Parameters: |
|
|---|
com_server.start_conns
com_server.server.start_conns(logger, *routes, *, logfile=None)
Initializes serial connections and disconnect handler
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
com_server.disconnect_conns
com_server.server.disconnect_conns(*routes)
Disconnects all Connection objects in provided ConnectionRoutes objects
It is recommended to call this after start_app() to make sure that the serial
connections are closed.
Note that calling this will exit the program using sys.exit().
Args
*routes (ConnectionRoutes): The ConnectionRoutes objects to disconnect connections from
com_server.all_ports
com_server.tools.all_ports(**kwargs)
Gets all ports from serial interface.
Gets ports from Serial interface by calling serial.tools.list_ports.comports().
See here for more info.
com_server.api
This is the server API module for the built-in endpoints of COM-Server.
See the Server API.
com_server.SendQueue
The send queue object
This object is like a queue but cannot be iterated through.
It contains methods such as front() and pop(), just like
the queue data structure in C++. However, objects cannot
be added to it because objects should only be added through
the send() method.
Makes sure the user only reads and pops from send queue and does not directly add or delete anything from the queue.
__init__(self, send_queue)
special
Constructor for send queue object
| Parameters: |
|
|---|
copy(self)
Returns a shallow copy of the send queue list
Using this to copy to a list may be dangerous, as
altering elements in the list may alter the elements
in the send queue itself. To prevent this, use the
deepcopy() method.
| Returns: |
|
|---|
deepcopy(self)
Returns a deepcopy of the send queue list
By using this, you can modify the list without altering any elements of the actual send queue itself. However, it is more resource intensive.
| Returns: |
|
|---|
front(self)
Returns the first element of the send queue
Raises IndexError: If length of send queue is 0
| Returns: |
|
|---|
pop(self)
Removes the first index from the queue.
| Exceptions: |
|
|---|
com_server.ReceiveQueue
The ReceiveQueue object.
This object is a queue, but the user can only add bytes object(s) to it.
Makes sure the user does not directly add, delete, or modify the queue.
__init__(self, rcv_queue, queue_size)
special
Constructor for send queue object.
| Parameters: |
|
|---|
copy(self)
Returns a shallow copy of the receive queue list
The receive queue list will be a list of tuples: - (timestamp, bytes data)
Using this to copy to a list may be dangerous, as
altering elements in the list may alter the elements
in the receive queue itself. To prevent this, use the
deepcopy() method.
| Returns: |
|
|---|
deepcopy(self)
Returns a deepcopy of the receive queue.
The receive queue list will be a list of tuples: - (timestamp, bytes data)
By using this, you can modify the list without altering any elements of the actual send queue itself. However, it is a little more resource intensive.
| Returns: |
|
|---|
pushitems(self, *args)
Adds a list of items to the receive queue
If the size exceeds queue_size when adding, then
it will pop the front of the queue.
A tuple (timestamp, bytes) will be added. The timestamp will be regenerated for each iteration of the for loop so they will be in order when binary searching.
| Parameters: |
|
|---|
| Exceptions: |
|
|---|
Constants
NO_TIMEOUT = float("inf")
Use this if you do not want a timeout. Not recommended.
NO_SEND_INTERVAL = 0
Use this if you do not want a send interval. Not recommended.
NORMAL_BAUD_RATE = 9600
FAST_BAUD_RATE = 115200
Standard baud rates that are commonly used.
NO_RCV_QUEUE = 1
RCV_QUEUE_SIZE_XSMALL = 32
RCV_QUEUE_SIZE_SMALL = 128
RCV_QUEUE_SIZE_NORMAL = 256
RCV_QUEUE_SIZE_LARGE = 512
RCV_QUEUE_SIZE_XLARGE = 1024
Different receive queue sizes for queue_size. Default is RCV_QUEUE_SIZE_NORMAL.
DEFAULT_HOST="0.0.0.0"
DEFAULT_PORT=8080
Default host and port for the server.
Exceptions
com_server.ConnectException
This exception is raised whenever a user tries to do an operation with the Connection class while it is disconnected, but the operation requires it to be connected, or vice versa.
com_server.EndpointExistsException
This exception is raised if the user tries to add a route to the RestApiHandler that already exists.
com_server.DuplicatePortException
Raised when trying to start a server with two or more ConnectionRoutes objects (experimental) and any of them share a common serial port. This is needed to prevent confusion when reconnecting to the ports.
Old Classes
Use of these is not recommended because these will be deprecated soon.
com_server.RestApiHandler
NOTE: This will not be supported for versions >=0.2. Use ConnectionRoutes instead.
A handler for creating endpoints with the Connection and Connection-based objects.
This class provides the framework for adding custom endpoints for doing
custom things with the serial connection and running the local server
that will host the API. It uses a flask_restful object as its back end.
Note that endpoints cannot have the names /register or /recall.
Additionally, resource classes have to extend the custom ConnectionResource class
from this library, not the Resource from flask_restful.
500 Internal Server Errors will occur with endpoints dealing with the connection
if the serial port is disconnected. The server will spawn another thread that will
immediately try to reconnect the serial port if it is disconnected. However, note
that the receive and send queues will reset when the serial port is disconnected.
If another process accesses an endpoint while another is
currently being used, then it will respond with
503 Service Unavailable.
More information on Flask and flask-restful
Register and recall endpoints:
/register(GET): An endpoint to register an IP; other endpoints will result in400status code if they are accessed without accessing this first (unlesshas_register_recallis False); if an IP is already registered then this will result in400; IPs must call this first before accessing serial port (unlesshas_register_recallis False)/recall(GET): After registered, can call/recallto "free" IP from server, allowing other IPs to call/registerto use the serial port
RestApiHandler.__init__()
def __init__(conn, has_register_recall=True, add_cors=False, catch_all_404s=True, **kwargs)
Constructor for class
Parameters:
conn(Connection): TheConnectionobject the API is going to be associated with.has_register_recall(bool): If False, removes the/registerand/recallendpoints so the user will not have to use them in order to access the other endpoints of the API. That is, visiting endpoints will not respond with a 400 status code even if/registerwas not accessed. By default True.add_cors(bool): If True, then the Flask app will have cross origin resource sharing enabled. By default False.catch_all_404s(bool): If True, then there will be JSON response for 404 errors. Otherwise, there will be a normal HTML response on 404. By default True.**kwargs, will be passed toflask_restful.Api(). See here for more info.
RestApiHandler.add_endpoint()
def add_endpoint(endpoint)
Decorator that adds an endpoint
This decorator should go above a class that
extends ConnectionResource. The class should
contain implementations of request methods such as
get(), post(), etc. similar to the Resource
class from flask_restful. To use the connection
object, use the self.conn attribute of the class
under the decorator.
For more information, see the flask_restful documentation.
Note that duplicate endpoints will result in an exception.
If there are two classes of the same name, even in different
endpoints, the program will append underscores to the name
until there are no more repeats. For example, if one class is
named "Hello" and another class is also named "Hello",
then the second class name will be changed to "Hello_".
This happens because flask_restful interprets duplicate class
names as duplicate endpoints.
If another process accesses an endpoint while another is
currently being used, then it will respond with
503 Service Unavailable.
Parameters:
endpoint: The endpoint to the resource. Cannot repeat./registerand/recallcannot be used, even ifhas_register_recallis False
RestApiHandler.add_resource()
def add_resource(*args, **kwargs)
Calls flask_restful.add_resource.
Allows adding endpoints that do not interact with the serial port.
See here
for more info on add_resource and here
for more info on flask_restful in general.
RestApiHandler.run()
def run(**kwargs)
Launches the Flask app as a Waitress production server (recommended).
Parameters:
logfile(str, None): The path of the file to log serial disconnect and reconnect events to. Leave as None if you do not want to log to a file. By default None.
All arguments in **kwargs will be passed to waitress.serve().
For more information, see here.
For Waitress documentation, see here.
If nothing is included, then runs on http://0.0.0.0:8080
Automatically disconnects the Connection object after
the server is closed.
RestApiHandler.run_dev()
def run_dev(**kwargs)
Launches the Flask app as a development server.
Not recommended because this is slower, and development features
such as debug mode and restarting do not work most of the time.
Use run() instead.
Parameters:
logfile(str, None): The path of the file to log serial disconnect and reconnect events to. Leave as None if you do not want to log to a file. By default None.
All arguments in **kwargs will be passed to Flask.run().
For more information, see here.
For documentation on Flask in general, see here.
Automatically disconnects the Connection object after
the server is closed.
Some arguments include:
host: The host of the server. Ex:localhost,0.0.0.0,127.0.0.1, etc.port: The port to host it on. Ex:5000(default),8000,8080, etc.debug: If the app should be used in debug mode. Very unreliable and most likely will not work.
RestApiHandler.run_prod()
def run_prod(**kwargs)
Same as run() but here for backward compatibility.
RestApiHandler.flask_obj
Getter:
- Gets the
Flaskobject that is the backend of the endpoints and the server.
This can be used to modify and customize the Flask object in this class.
RestApiHandler.api_obj
Getter:
- Gets the
flask_restfulAPI object that handles parsing the classes.
This can be used to modify and customize the Api object in this class.