# Path: uchroma/log.py
# LOG_PROTOCOL_TRACE = 4
# 
# Path: uchroma/util.py
# def smart_delay(delay: float, last_cmd: float, remain: int=0) -> float:
#     """
#     A "smart" delay mechanism which tries to reduce the
#     delay as much as possible based on the time the last
#     delay happened.
# 
#     :param delay: delay in seconds
#     :param last_cmd: time of last command
#     :param remain: counter, skip delay unless it's zero
# 
#     :return: timestamp to feed to next invocation
#     """
#     now = time.monotonic()
# 
#     if remain == 0 and last_cmd is not None and delay > 0.0:
# 
#         delta = now - last_cmd
#         if delta < delay:
#             sleep = delay - delta
#             time.sleep(sleep)
# 
#     return now
# 
# Path: uchroma/server/byte_args.py
# class ByteArgs:
#     """
#     Helper class for assembling byte arrays from
#     argument lists of varying types
#     """
#     def __init__(self, size, data=None):
#         self._data_ptr = 0
# 
#         if data is None:
#             self._data = np.zeros(shape=(size,), dtype=np.uint8)
#         else:
#             self._data = np.frombuffer(data, dtype=np.uint8)
# 
# 
#     @property
#     def data(self):
#         """
#         The byte array assembled from supplied arguments
#         """
#         return self._data
# 
# 
#     @property
#     def size(self):
#         """
#         Size of the byte array
#         """
#         return len(self._data)
# 
# 
#     def _ensure_space(self, size):
#         assert len(self._data) > size + self._data_ptr, \
#                 ('Additional argument (len=%d) would exceed size limit %d (cur=%d)'
#                  % (size, len(self._data), self._data_ptr))
# 
# 
#     def clear(self):
#         """
#         Empty the contents of the array
# 
#         :return: The empty ByteArgs
#         :rtype: ByteArgs
#         """
#         self._data.fill(0)
#         self._data_ptr = 0
#         return self
# 
# 
#     def put(self, arg, packing=None):
#         """
#         Add an argument to this array
# 
#         :param arg: The argument to append
#         :type arg: varies
# 
#         :param packing: The representation passed to struct.pack
#         :type packing: str
# 
#         :return: This ByteArgs instance
#         :rtype: ByteArgs
#         """
#         data = None
#         if packing is not None:
#             data = struct.pack(packing, arg)
#         elif isinstance(arg, Color):
#             data = struct.pack("=BBB", *arg.intTuple)
#         elif isinstance(arg, Enum):
#             if hasattr(arg, "opcode"):
#                 data = arg.opcode
#             else:
#                 data = arg.value
#         elif isinstance(arg, np.ndarray):
#             data = arg.flatten()
#         elif isinstance(arg, (bytearray, bytes)):
#             data = arg
#         else:
#             data = struct.pack("=B", arg)
# 
#         if isinstance(data, int):
#             if self._data_ptr + 1 > len(self._data):
#                 raise ValueError('No space left in argument list')
# 
#             self._ensure_space(1)
#             self._data[self._data_ptr] = data
#             self._data_ptr += 1
#         else:
#             datalen = len(data)
#             if datalen > 0:
#                 if self._data_ptr + datalen > len(self._data):
#                     raise ValueError('No space left in argument list')
# 
#                 if not isinstance(data, np.ndarray):
#                     data = np.frombuffer(data, dtype=np.uint8)
#                 self._data[self._data_ptr:self._data_ptr+datalen] = data
#                 self._data_ptr += datalen
# 
#         return self
# 
# 
#     def put_all(self, args, packing=None):
#         for arg in args:
#             self.put(arg, packing=packing)
#         return self
# 
# 
#     def put_short(self, arg):
#         """
#         Convenience method to add an argument as a short to
#         the array
# 
#         :param arg: The argument to append
#         :type arg: varies
# 
#         :return: This ByteArgs instance
#         :rtype: ByteArgs
#         """
#         return self.put(arg, '=H')
# 
# 
#     def put_int(self, arg):
#         """
#         Convenience method to add an argument as an integer to
#         the array
# 
#         :param arg: The argument to append
#         :type arg: varies
# 
#         :return: This ByteArgs instance
#         :rtype: ByteArgs
#         """
#         return self.put(arg, '=I')


import struct
import time
import numpy as np
from enum import Enum
from uchroma.log import LOG_PROTOCOL_TRACE
from uchroma.util import smart_delay
from .byte_args import ByteArgs
from ._crc import fast_crc
                    self._hexdump(resp, '<-- ')
                    if self._unpack_response(resp):
                        if timeout_cb is not None:
                            timeout_cb(self.status, None)
                        return True

                    if self.status == Status.FAIL or self.status == Status.UNSUPPORTED:
                        self._logger.error("Command failed with status %s",
                                           self.status.name)
                        return False

                    if timeout_cb is not None and self.status == Status.TIMEOUT:
                        timeout_cb(self.status, self.result)
                        return False

                    self._logger.warning("Retrying request due to status %s (%d)",
                                         self.status.name, retry_count)

                    time.sleep(0.1)

                    retry_count -= 1

                except (OSError, IOError):
                    self._status = Status.OSERROR
                    raise

        return False


    @property

# Path: uchroma/log.py
# LOG_PROTOCOL_TRACE = 4
# 
# Path: uchroma/util.py
# def smart_delay(delay: float, last_cmd: float, remain: int=0) -> float:
#     """
#     A "smart" delay mechanism which tries to reduce the
#     delay as much as possible based on the time the last
#     delay happened.
# 
#     :param delay: delay in seconds
#     :param last_cmd: time of last command
#     :param remain: counter, skip delay unless it's zero
# 
#     :return: timestamp to feed to next invocation
#     """
#     now = time.monotonic()
# 
#     if remain == 0 and last_cmd is not None and delay > 0.0:
# 
#         delta = now - last_cmd
#         if delta < delay:
#             sleep = delay - delta
#             time.sleep(sleep)
# 
#     return now
# 
# Path: uchroma/server/byte_args.py
# class ByteArgs:
#     """
#     Helper class for assembling byte arrays from
#     argument lists of varying types
#     """
#     def __init__(self, size, data=None):
#         self._data_ptr = 0
# 
#         if data is None:
#             self._data = np.zeros(shape=(size,), dtype=np.uint8)
#         else:
#             self._data = np.frombuffer(data, dtype=np.uint8)
# 
# 
#     @property
#     def data(self):
#         """
#         The byte array assembled from supplied arguments
#         """
#         return self._data
# 
# 
#     @property
#     def size(self):
#         """
#         Size of the byte array
#         """
#         return len(self._data)
# 
# 
#     def _ensure_space(self, size):
#         assert len(self._data) > size + self._data_ptr, \
#                 ('Additional argument (len=%d) would exceed size limit %d (cur=%d)'
#                  % (size, len(self._data), self._data_ptr))
# 
# 
#     def clear(self):
#         """
#         Empty the contents of the array
# 
#         :return: The empty ByteArgs
#         :rtype: ByteArgs
#         """
#         self._data.fill(0)
#         self._data_ptr = 0
#         return self
# 
# 
#     def put(self, arg, packing=None):
#         """
#         Add an argument to this array
# 
#         :param arg: The argument to append
#         :type arg: varies
# 
#         :param packing: The representation passed to struct.pack
#         :type packing: str
# 
#         :return: This ByteArgs instance
#         :rtype: ByteArgs
#         """
#         data = None
#         if packing is not None:
#             data = struct.pack(packing, arg)
#         elif isinstance(arg, Color):
#             data = struct.pack("=BBB", *arg.intTuple)
#         elif isinstance(arg, Enum):
#             if hasattr(arg, "opcode"):
#                 data = arg.opcode
#             else:
#                 data = arg.value
#         elif isinstance(arg, np.ndarray):
#             data = arg.flatten()
#         elif isinstance(arg, (bytearray, bytes)):
#             data = arg
#         else:
#             data = struct.pack("=B", arg)
# 
#         if isinstance(data, int):
#             if self._data_ptr + 1 > len(self._data):
#                 raise ValueError('No space left in argument list')
# 
#             self._ensure_space(1)
#             self._data[self._data_ptr] = data
#             self._data_ptr += 1
#         else:
#             datalen = len(data)
#             if datalen > 0:
#                 if self._data_ptr + datalen > len(self._data):
#                     raise ValueError('No space left in argument list')
# 
#                 if not isinstance(data, np.ndarray):
#                     data = np.frombuffer(data, dtype=np.uint8)
#                 self._data[self._data_ptr:self._data_ptr+datalen] = data
#                 self._data_ptr += datalen
# 
#         return self
# 
# 
#     def put_all(self, args, packing=None):
#         for arg in args:
#             self.put(arg, packing=packing)
#         return self
# 
# 
#     def put_short(self, arg):
#         """
#         Convenience method to add an argument as a short to
#         the array
# 
#         :param arg: The argument to append
#         :type arg: varies
# 
#         :return: This ByteArgs instance
#         :rtype: ByteArgs
#         """
#         return self.put(arg, '=H')
# 
# 
#     def put_int(self, arg):
#         """
#         Convenience method to add an argument as an integer to
#         the array
# 
#         :param arg: The argument to append
#         :type arg: varies
# 
#         :return: This ByteArgs instance
#         :rtype: ByteArgs
#         """
#         return self.put(arg, '=I')


# Path: uchroma/server/report.py
import struct
import time
import numpy as np
from enum import Enum
from uchroma.log import LOG_PROTOCOL_TRACE
from uchroma.util import smart_delay
from .byte_args import ByteArgs
from ._crc import fast_crc

                    self._hexdump(resp, '<-- ')
                    if self._unpack_response(resp):
                        if timeout_cb is not None:
                            timeout_cb(self.status, None)
                        return True

                    if self.status == Status.FAIL or self.status == Status.UNSUPPORTED:
                        self._logger.error("Command failed with status %s",
                                           self.status.name)
                        return False

                    if timeout_cb is not None and self.status == Status.TIMEOUT:
                        timeout_cb(self.status, self.result)
                        return False

                    self._logger.warning("Retrying request due to status %s (%d)",
                                         self.status.name, retry_count)

                    time.sleep(0.1)

                    retry_count -= 1

                except (OSError, IOError):
                    self._status = Status.OSERROR
                    raise

        return False


    @property

    def args(self) -> ByteArgs: