restructuring towards python client library #455 (#613)

* python client library clean branch for PR. #455

* removed superfluous file. #455

* removed one more superfluous file. #455

* README.md, .pylintrc and pylint based fixes. #455

* updated wrt. to the review comments. #455

* removed pylint documentation duplication. #455
This commit is contained in:
Benjamin Zeiss
2022-01-22 00:08:29 +01:00
committed by GitHub
parent 7f362c9308
commit 089dc302e5
38 changed files with 1573 additions and 1467 deletions
-1
View File
@@ -1 +0,0 @@
../.pylintrc
-11
View File
@@ -33,17 +33,6 @@ The start.sh script can also be run standalone, and will handle the venv creatio
$ ./start.sh --rotation=180 --height=64
```
## Static analysis with pylint
It is recommended to run pylint against new code to protect against bugs
and keep the code readable and maintainable.
The local pylint configuration lives in .pylintrc (symlink to ../.pylintrc)
```
$ sudo apt install pylint3
$ pylint3 python_source_file.py
```
## Credits
### type_writer.ttf
* _Type Writer_ TrueType font by Mandy Smith
View File
+2 -1
View File
@@ -3,7 +3,8 @@ Linux interrupt handling module
"""
import signal
class GracefulInterruptHandler():
class GracefulInterruptHandler:
"""
Class for handling Linux signal interrupts
"""
+1
View File
@@ -2,6 +2,7 @@
Module with methods that interact with the Pi's Linux system
"""
def get_ip_and_host():
"""
Use a mock socket connection to identify the Pi's hostname and IP address
-70
View File
@@ -1,70 +0,0 @@
"""
Module for commands sent to the RaSCSI backend service.
"""
from os import path
from unidecode import unidecode
from socket_cmds import send_pb_command
import rascsi_interface_pb2 as proto
def device_list(token):
"""
Sends a DEVICES_INFO command to the server.
Returns a list of dicts with info on all attached devices.
"""
command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICES_INFO
command.params["token"] = token
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
result.ParseFromString(data)
dlist = []
i = 0
while i < len(result.devices_info.devices):
did = result.devices_info.devices[i].id
dtype = proto.PbDeviceType.Name(result.devices_info.devices[i].type)
dstat = result.devices_info.devices[i].status
dprop = result.devices_info.devices[i].properties
# Building the status string
dstat_msg = []
if dstat.protected and dprop.protectable:
dstat_msg.append("Write-Protected")
if dstat.removed and dprop.removable:
dstat_msg.append("No Media")
if dstat.locked and dprop.lockable:
dstat_msg.append("Locked")
# Transliterate non-ASCII chars in the file name to ASCII
dfile = unidecode(path.basename(result.devices_info.devices[i].file.name))
dven = result.devices_info.devices[i].vendor
dprod = result.devices_info.devices[i].product
dlist.append({
"id": did,
"device_type": dtype,
"status": ", ".join(dstat_msg),
"file": dfile,
"vendor": dven,
"product": dprod,
})
i += 1
return dlist
def is_token_auth(token):
"""
Sends a CHECK_AUTHENTICATION command to the server.
Tells you whether RaSCSI backend is protected by a token password or not.
Returns (bool) status and (str) msg.
"""
command = proto.PbCommand()
command.operation = proto.PbOperation.CHECK_AUTHENTICATION
command.params["token"] = token
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
result.ParseFromString(data)
return {"status": result.status, "msg": result.msg}
+22 -3
View File
@@ -30,6 +30,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import argparse
import sys
from time import sleep
@@ -39,7 +40,8 @@ from adafruit_ssd1306 import SSD1306_I2C
from PIL import Image, ImageDraw, ImageFont
from interrupt_handler import GracefulInterruptHandler
from pi_cmds import get_ip_and_host
from ractl_cmds import device_list, is_token_auth
from rascsi.ractl_cmds import RaCtlCmds
from rascsi.socket_cmds import SocketCmds
parser = argparse.ArgumentParser(description="RaSCSI OLED Monitor script")
parser.add_argument(
@@ -65,6 +67,20 @@ parser.add_argument(
action="store",
help="Token password string for authenticating with RaSCSI",
)
parser.add_argument(
"--rascsi-host",
type=str,
default="localhost",
action="store",
help="RaSCSI host. Default: localhost",
)
parser.add_argument(
"--rascsi-port",
type=str,
default=6868,
action="store",
help="RaSCSI port. Default: 6868",
)
args = parser.parse_args()
if args.rotation == 0:
@@ -81,6 +97,9 @@ elif args.height == 32:
TOKEN = args.password
sock_cmd = SocketCmds(host=args.rascsi_host, port=args.rascsi_port)
ractl_cmd = RaCtlCmds(sock_cmd=sock_cmd, token=TOKEN)
WIDTH = 128
BORDER = 5
@@ -148,10 +167,10 @@ def formatted_output():
Formats the strings to be displayed on the Screen
Returns a (list) of (str) output
"""
rascsi_list = device_list(TOKEN)
rascsi_list = ractl_cmd.list_devices()['device_list']
output = []
if not TOKEN and not is_token_auth(TOKEN)["status"]:
if not TOKEN and not ractl_cmd.is_token_auth()["status"]:
output.append("Permission denied!")
elif rascsi_list:
for line in rascsi_list:
-73
View File
@@ -1,73 +0,0 @@
"""
Module for handling socket connections for sending commands
and receiving results from the RaSCSI backend
"""
import socket
from struct import pack, unpack
from time import sleep
def send_pb_command(payload):
"""
Takes a (str) containing a serialized protobuf as argument.
Establishes a socket connection with RaSCSI.
"""
# Host and port number where rascsi is listening for socket connections
host = 'localhost'
port = 6868
counter = 0
tries = 20
error_msg = ""
while counter < tries:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((host, port))
return send_over_socket(sock, payload)
except socket.error as error:
counter += 1
print("The RaSCSI service is not responding - attempt %s/%s",
str(counter), str(tries))
error_msg = str(error)
sleep(0.2)
exit(error_msg)
def send_over_socket(sock, payload):
"""
Takes a socket object and (str) payload with serialized protobuf.
Sends payload to RaSCSI over socket and captures the response.
Tries to extract and interpret the protobuf header to get response size.
Reads data from socket in 2048 bytes chunks until all data is received.
"""
# Sending the magic word "RASCSI" to authenticate with the server
sock.send(b"RASCSI")
# Prepending a little endian 32bit header with the message size
sock.send(pack("<i", len(payload)))
sock.send(payload)
# Receive the first 4 bytes to get the response header
response = sock.recv(4)
if len(response) >= 4:
# Extracting the response header to get the length of the response message
response_length = unpack("<i", response)[0]
# Reading in chunks, to handle a case where the response message is very large
chunks = []
bytes_recvd = 0
while bytes_recvd < response_length:
chunk = sock.recv(min(response_length - bytes_recvd, 2048))
if chunk == b'':
exit("Socket connection has dropped unexpectedly. "
"RaSCSI may have crashed."
)
chunks.append(chunk)
bytes_recvd = bytes_recvd + len(chunk)
response_message = b''.join(chunks)
return response_message
exit("The response from RaSCSI did not contain a protobuf header. "
"RaSCSI may have crashed."
)
+3
View File
@@ -149,4 +149,7 @@ if [ -z ${HEIGHT+x} ]; then
else
echo "Starting with parameter $HEIGHT"
fi
PYTHON_COMMON_PATH=$(dirname $PWD)/common/src
export PYTHONPATH=$PWD/src:${PYTHON_COMMON_PATH}
python3 src/rascsi_oled_monitor.py ${ROTATION} ${HEIGHT} ${PASSWORD}