initialize
This commit is contained in:
169
LVX6048/powermon-patches/usbport.py
Normal file
169
LVX6048/powermon-patches/usbport.py
Normal file
@@ -0,0 +1,169 @@
|
||||
""" powermon / ports / usbport.py """
|
||||
# import asyncio
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from glob import glob
|
||||
|
||||
from powermon.commands.command import Command
|
||||
from powermon.commands.result import Result, ResultType
|
||||
from powermon.libs.errors import ConfigError, PowermonProtocolError
|
||||
from powermon.ports import PortType
|
||||
from powermon.ports.abstractport import AbstractPort, _AbstractPortDTO
|
||||
from powermon.protocols import get_protocol_definition
|
||||
|
||||
log = logging.getLogger("USBPort")
|
||||
|
||||
|
||||
class UsbPortDTO(_AbstractPortDTO):
|
||||
""" data transfer model for SerialPort class """
|
||||
path: str
|
||||
serial_number: None | int | str
|
||||
|
||||
|
||||
class USBPort(AbstractPort):
|
||||
""" usb port object """
|
||||
@classmethod
|
||||
async def from_config(cls, config=None):
|
||||
log.debug("building usb port. config:%s", config)
|
||||
path = config.get("path", "/dev/hidraw0")
|
||||
serial_number = config.get("serial_number")
|
||||
# get protocol handler, default to PI30 if not supplied
|
||||
protocol = get_protocol_definition(protocol=config.get("protocol", "PI30"))
|
||||
# instantiate class
|
||||
_class = cls(path=path, protocol=protocol)
|
||||
# deal with wildcard path resolution
|
||||
_class.path = await _class.resolve_path(path, serial_number)
|
||||
return _class
|
||||
|
||||
def __init__(self, path, protocol) -> None:
|
||||
self.port_type = PortType.USB
|
||||
super().__init__(protocol=protocol)
|
||||
|
||||
self.path = None
|
||||
self.port = None
|
||||
|
||||
|
||||
async def resolve_path(self, path, serial_number):
|
||||
"""Async method to resolve a valid path by testing each one."""
|
||||
# expand 'wildcard'
|
||||
paths = glob(path)
|
||||
if not paths:
|
||||
raise ConfigError(f"No matching paths found on this system for {path}")
|
||||
|
||||
if len(paths) == 1:
|
||||
return paths[0] # only one valid result
|
||||
|
||||
# More than one valid path
|
||||
# check we have something to look for
|
||||
if serial_number is None:
|
||||
raise ConfigError("Wildcard paths require a serial_number in config.")
|
||||
# check we have get_id in this protocol
|
||||
try:
|
||||
command = self.protocol.get_id_command()
|
||||
except PowermonProtocolError as ex:
|
||||
raise ConfigError(f"No get_id in protocol: {self.protocol.protocol_id}") from ex
|
||||
|
||||
for _path in paths:
|
||||
log.debug("Checking path: %s for serial_number: %s", _path, serial_number)
|
||||
self.path = _path
|
||||
await self.connect()
|
||||
res = await self.send_and_receive(command=command)
|
||||
await self.disconnect()
|
||||
|
||||
if res.is_valid and str(res.readings[0].data_value) == str(serial_number):
|
||||
log.info("SUCCESS: path: %s matches serial_number: %s", _path, serial_number)
|
||||
return _path # return the matching path
|
||||
raise ConfigError(f"None of the paths match serial_number: {serial_number}")
|
||||
|
||||
|
||||
def to_dto(self):
|
||||
dto = UsbPortDTO(port_type="usb", path=self.path, protocol=self.protocol.to_dto())
|
||||
return dto
|
||||
|
||||
def is_connected(self) -> bool:
|
||||
return self.port is not None
|
||||
|
||||
async def connect(self) -> bool:
|
||||
if self.is_connected():
|
||||
log.debug("USBPort already connected")
|
||||
return True
|
||||
log.debug("USBPort connecting. path:%s, protocol:%s", self.path, self.protocol)
|
||||
try:
|
||||
self.port = os.open(self.path, os.O_RDWR | os.O_NONBLOCK)
|
||||
log.debug("USBPort port number $%s", self.port)
|
||||
except Exception as e:
|
||||
log.warning("Error openning usb port: %s", e)
|
||||
self.port = None
|
||||
self.error_message = e
|
||||
return self.is_connected()
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
log.debug("USBPort disconnecting: %i", self.port)
|
||||
if self.port is not None:
|
||||
os.close(self.port)
|
||||
self.port = None
|
||||
|
||||
async def send_and_receive(self, command: Command) -> Result:
|
||||
if not self.is_connected():
|
||||
log.warning("USBPort not connected")
|
||||
return command.build_result(result_type=ResultType.ERROR, raw_response=b"USBPort not connected", protocol=self.protocol)
|
||||
response_line = bytes()
|
||||
|
||||
# Drain any leftover bytes from previous command's response so we
|
||||
# don't parse them as this command's reply.
|
||||
try:
|
||||
while True:
|
||||
stale = os.read(self.port, 256)
|
||||
if not stale:
|
||||
break
|
||||
log.debug("drained %i stale bytes: %s", len(stale), stale)
|
||||
except BlockingIOError:
|
||||
pass
|
||||
|
||||
# Send the command to the open usb connection
|
||||
full_command = command.full_command
|
||||
cmd_len = len(full_command)
|
||||
log.debug("length of to_send: %i", cmd_len)
|
||||
# for command of len < 8 it ok just to send
|
||||
# otherwise need to pack to a multiple of 8 bytes and send 8 at a time
|
||||
try:
|
||||
if cmd_len <= 8:
|
||||
# Send all at once
|
||||
log.debug("sending full_command in on shot")
|
||||
time.sleep(0.05)
|
||||
os.write(self.port, full_command)
|
||||
else:
|
||||
log.debug("multiple chunk send")
|
||||
chunks = [full_command[i:i + 8] for i in range(0, cmd_len, 8)]
|
||||
for chunk in chunks:
|
||||
# pad chunk to 8 bytes
|
||||
if len(chunk) < 8:
|
||||
padding = 8 - len(chunk)
|
||||
chunk += b'\x00' * padding
|
||||
log.debug("sending chunk: %s", chunk)
|
||||
time.sleep(0.05)
|
||||
os.write(self.port, chunk)
|
||||
time.sleep(0.25)
|
||||
# Read from the usb connection
|
||||
# try to a max of 100 times
|
||||
for _ in range(100):
|
||||
# attempt to deal with resource busy and other failures to read
|
||||
time.sleep(0.15)
|
||||
try:
|
||||
r = os.read(self.port, 256)
|
||||
except BlockingIOError:
|
||||
continue
|
||||
response_line += r
|
||||
# Finished is \r is in byte_response
|
||||
if bytes([13]) in response_line:
|
||||
# remove anything after the \r
|
||||
response_line = response_line[: response_line.find(bytes([13])) + 1]
|
||||
break
|
||||
except BrokenPipeError as e:
|
||||
log.debug("USB read error: %s", e)
|
||||
log.debug("usb response was: %s", response_line)
|
||||
# response = self.get_protocol().check_response_and_trim(response_line)
|
||||
result = command.build_result(raw_response=response_line, protocol=self.protocol)
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user