From 9a8360317711ee9515ad36965b9c231d48154cb8 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Tue, 21 Jun 2022 11:55:34 -0700 Subject: [PATCH 1/6] adding buffer client --- jacdac/bus.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/jacdac/bus.py b/jacdac/bus.py index ad1cd1b..8101ce3 100644 --- a/jacdac/bus.py +++ b/jacdac/bus.py @@ -1784,3 +1784,53 @@ def process_packet(self, pkt: JDPacket): # log(`handle pkt at ${client.role} rep=${pkt.serviceCommand}`) c.device = self c.handle_packet_outer(pkt) + +class BufferClient(Client): + _value: bytes + _dirty: bool + + """ + A client that handles a double-buffer bytes buffer + """ + def __init__(self, bus: Bus, service_class: int, pack_formats: Dict[int, str], role: str) -> None: + super().__init__(bus, service_class, pack_formats, role) + + self._value = bytearray(0) + self._dirty = False + + @property + def value(self) -> bytes: + """ + Cached reading value + """ + return self._value + + @value.setter + def value(self, v: bytes) -> None: + # TODO: check for equality + self._value = v or bytearray(0) + self._dirty = True + # TODO: debounce + self.refresh_value() + + @property + def dirty(self) -> bool: + return self._dirty + + @dirty.setter + def set_dirty(self) -> None: + self._dirty = True + + def refresh_value(self) -> None: + self.register(JD_REG_VALUE).set_values(self._value) + self._dirty = False + + def update_value_length(self, length: Optional[int]) -> None: + l = len(self._value) + if (not length is None) and l != length: + # harmonize lengths + if length > l: + v = self._value + bytearray(length - l) + else: + v = self._value[0:length -1] + From 2ac7106534a8a48e06b65209f556ad5357374b4a Mon Sep 17 00:00:00 2001 From: pelikhan Date: Tue, 21 Jun 2022 13:38:50 -0700 Subject: [PATCH 2/6] fix blinky --- examples/blinky.py | 9 ++++----- jacdac/bus.py | 20 ++++++++++++-------- jacdac/led/client.py | 42 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/examples/blinky.py b/examples/blinky.py index 46a5f5c..b14f46d 100644 --- a/examples/blinky.py +++ b/examples/blinky.py @@ -1,21 +1,20 @@ from jacdac import Bus from jacdac.led import LedClient -from jacdac import LoggerPriority from time import sleep if __name__ == '__main__': def main(): bus = Bus() led = LedClient(bus, "led") - speed = 0 - brightness = 128 + led.brightness = 0.5 # fade between colors while True: + print("blink") # blue - led.animate(0, 0, brightness, speed) + led.set_all(0xff0000) sleep(1) # off - led.animate(brightness, 0, 0, speed) + led.set_all(0x000000) sleep(1) main() diff --git a/jacdac/bus.py b/jacdac/bus.py index 8101ce3..3861426 100644 --- a/jacdac/bus.py +++ b/jacdac/bus.py @@ -1786,7 +1786,7 @@ def process_packet(self, pkt: JDPacket): c.handle_packet_outer(pkt) class BufferClient(Client): - _value: bytes + _value: bytearray _dirty: bool """ @@ -1799,14 +1799,14 @@ def __init__(self, bus: Bus, service_class: int, pack_formats: Dict[int, str], r self._dirty = False @property - def value(self) -> bytes: + def value(self) -> bytearray: """ Cached reading value """ return self._value @value.setter - def value(self, v: bytes) -> None: + def value(self, v: bytearray) -> None: # TODO: check for equality self._value = v or bytearray(0) self._dirty = True @@ -1817,20 +1817,24 @@ def value(self, v: bytes) -> None: def dirty(self) -> bool: return self._dirty - @dirty.setter def set_dirty(self) -> None: self._dirty = True def refresh_value(self) -> None: - self.register(JD_REG_VALUE).set_values(self._value) - self._dirty = False + if self._dirty: + print(self._value) + self.register(JD_REG_VALUE).set_values(self._value) + self._dirty = False def update_value_length(self, length: Optional[int]) -> None: l = len(self._value) if (not length is None) and l != length: # harmonize lengths if length > l: - v = self._value + bytearray(length - l) + self._value = self._value + bytearray(length - l) + self._dirty = True else: - v = self._value[0:length -1] + self._value = self._value[0:length -1] + self._dirty = True + diff --git a/jacdac/led/client.py b/jacdac/led/client.py index 3aa41fe..a90ece2 100644 --- a/jacdac/led/client.py +++ b/jacdac/led/client.py @@ -1,10 +1,10 @@ # Autogenerated file. Do not edit. -from jacdac.bus import Bus, Client +from jacdac.bus import Bus, BufferClient from .constants import * from typing import Optional -class LedClient(Client): +class LedClient(BufferClient): """ A controller for small displays of individually controlled RGB LEDs. * @@ -25,12 +25,11 @@ def pixels(self) -> Optional[bytes]: When writing, if the buffer is too short, the remaining pixels are set to `#000000`; if the buffer is too long, the write may be ignored, or the additional pixels may be ignored., """ - return self.register(JD_LED_REG_PIXELS).value() + return self.value @pixels.setter def pixels(self, value: bytes) -> None: - self.register(JD_LED_REG_PIXELS).set_values(value) - + self.value @property def brightness(self) -> Optional[float]: @@ -110,4 +109,37 @@ def variant(self) -> Optional[LedVariant]: """ return self.register(JD_LED_REG_VARIANT).value() + def _sync(self): + self.register(JD_LED_REG_NUM_PIXELS).refresh() + n = self.num_pixels + if not n is None: + self.update_value_length(n * 3) + + def show(self): + """ + Sends the buffer information to the server + """ + self._sync() + self.refresh_value() + + + def set_all(self, rgb: int): + """ + Sets all the colors to particular color + """ + self._sync() + r= (rgb >> 16) & 0xff + g = (rgb >> 8) & 0xff + b = (rgb >> 0) & 0xff + buf = self.value + dirty = self.dirty + for i in range(0,len(buf),3): + dirty = dirty or buf[i] != r or buf[i + 1] != g or buf[i + 2] != b + buf[i] = r + buf[i + 1] = g + buf[i + 2] = b + if dirty: + self.set_dirty() + self.show() + From efc9ceb3ce3d9dd57942246665542cf0e21af823 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Tue, 21 Jun 2022 15:45:34 -0700 Subject: [PATCH 3/6] some cleanup --- examples/fade.py | 11 +++++------ examples/slidy_blinky.py | 12 ++++-------- jacdac/base/__init__.py | 2 -- jacdac/base/constants.py | 6 ------ jacdac/bus.py | 17 +++-------------- jacdac/sensor/__init__.py | 2 -- jacdac/sensor/constants.py | 6 ------ jacdac/transport.py | 12 ++++++++++++ jacdac/transports/exec.py | 2 +- jacdac/transports/hf2.py | 2 +- jacdac/transports/spi.py | 2 +- jacdac/transports/ws.py | 2 +- setup.py | 2 +- 13 files changed, 29 insertions(+), 49 deletions(-) delete mode 100644 jacdac/base/__init__.py delete mode 100644 jacdac/base/constants.py delete mode 100644 jacdac/sensor/__init__.py delete mode 100644 jacdac/sensor/constants.py create mode 100644 jacdac/transport.py diff --git a/examples/fade.py b/examples/fade.py index cca7f57..9d40400 100644 --- a/examples/fade.py +++ b/examples/fade.py @@ -6,18 +6,17 @@ def main(): bus = Bus() led = LedClient(bus, "led") - speed = 16 - brightness = 128 - # fade between colors + led.brightness = 0.5 + # change between colors while True: # blue - led.animate(0, 0, brightness, speed) + led.set_all(0x0000ff) sleep(1) # red - led.animate(brightness, 0, 0, speed) + led.set_all(0xff0000) sleep(1) # green - led.animate(0, brightness, 0, speed) + led.set_all(0x00ff00) sleep(1) main() diff --git a/examples/slidy_blinky.py b/examples/slidy_blinky.py index b81c9a6..1e7d135 100644 --- a/examples/slidy_blinky.py +++ b/examples/slidy_blinky.py @@ -8,17 +8,13 @@ def main(): bus = Bus() led = LedClient(bus, "led") slider = PotentiometerClient(bus, "slider") - speed = 16 - brightness = 128 + led.set_all(0x0000ff) # fade between colors while True: position = slider.position or 0. - brightness = int(position / 100. * 255.) - # blue - led.animate(0, 0, brightness, speed) - sleep(1) - # off - led.animate(brightness, 0, 0, speed) + brightness = position / 100. + # change brightness + led.brightness = brightness sleep(1) main() diff --git a/jacdac/base/__init__.py b/jacdac/base/__init__.py deleted file mode 100644 index c43a639..0000000 --- a/jacdac/base/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Autogenerated file. -from .constants import * diff --git a/jacdac/base/constants.py b/jacdac/base/constants.py deleted file mode 100644 index 3f24d8f..0000000 --- a/jacdac/base/constants.py +++ /dev/null @@ -1,6 +0,0 @@ -# Autogenerated constants for Base service service -from jacdac.constants import * -from jacdac.system.constants import * -JD_PACK_FORMATS = { - -} diff --git a/jacdac/bus.py b/jacdac/bus.py index 3861426..7b399d4 100644 --- a/jacdac/bus.py +++ b/jacdac/bus.py @@ -22,6 +22,7 @@ from .role_manager.constants import * from .unique_brain.constants import * from .packet import * +from .transport import Transport from .util import now, log, logv from .pack import PackTuple, PackType, jdpack, jdunpack @@ -179,18 +180,6 @@ def _service_matches(dev: 'Device', serv: bytearray): return False return True - -class Transport: - """A base class for packet transports""" - - on_receive: Optional[Callable[[bytes], None]] = None - # Callback to report a received packet to the bus - - def send(self, pkt: bytes) -> None: - # send a packet payload over the transport layer - pass - - def rand_u64(): return bytearray([getrandbits(8) for _ in range(8)]) @@ -1690,7 +1679,7 @@ def packet_count(self): @ property def is_connected(self): - return self.clients != None + return len(self.clients) == 0 @ property def short_id(self): @@ -1735,7 +1724,7 @@ def _destroy(self): self.debug("destroy") for c in self.clients: c._detach() - self.clients = None # type: ignore + self.clients = [] def _log_report_prefix(self) -> str: return "{}>".format(self.short_id) diff --git a/jacdac/sensor/__init__.py b/jacdac/sensor/__init__.py deleted file mode 100644 index c43a639..0000000 --- a/jacdac/sensor/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Autogenerated file. -from .constants import * diff --git a/jacdac/sensor/constants.py b/jacdac/sensor/constants.py deleted file mode 100644 index 030459d..0000000 --- a/jacdac/sensor/constants.py +++ /dev/null @@ -1,6 +0,0 @@ -# Autogenerated constants for Sensor service -from jacdac.constants import * -from jacdac.system.constants import * -JD_PACK_FORMATS = { - -} diff --git a/jacdac/transport.py b/jacdac/transport.py new file mode 100644 index 0000000..1e9a73b --- /dev/null +++ b/jacdac/transport.py @@ -0,0 +1,12 @@ +from typing import Callable, Optional + + +class Transport: + """A base class for packet transports""" + + on_receive: Optional[Callable[[bytes], None]] = None + # Callback to report a received packet to the bus + + def send(self, pkt: bytes) -> None: + # send a packet payload over the transport layer + pass \ No newline at end of file diff --git a/jacdac/transports/exec.py b/jacdac/transports/exec.py index b771aa1..93481bd 100644 --- a/jacdac/transports/exec.py +++ b/jacdac/transports/exec.py @@ -1,5 +1,5 @@ import threading -from jacdac.bus import Transport +from jacdac.transport import Transport import subprocess from jacdac.util import buf2hex, hex2buf diff --git a/jacdac/transports/hf2.py b/jacdac/transports/hf2.py index 2b63a62..119ed9d 100644 --- a/jacdac/transports/hf2.py +++ b/jacdac/transports/hf2.py @@ -5,7 +5,7 @@ import struct from typing import List -from jacdac.bus import Transport +from jacdac.transport import Transport HF2_CMD_INFO = 0x0002 HF2_CMD_DMESG = 0x0010 diff --git a/jacdac/transports/spi.py b/jacdac/transports/spi.py index 906ca49..5be3eaf 100644 --- a/jacdac/transports/spi.py +++ b/jacdac/transports/spi.py @@ -2,7 +2,7 @@ from typing import List, Optional from time import sleep from tokenize import Number -from jacdac.bus import Transport +from jacdac.transport import Transport from jacdac.util import buf2hex, hex2buf, now from gpiod import Chip, Line, LineBulk, LINE_REQ_EV_RISING_EDGE, LINE_REQ_FLAG_ACTIVE_LOW, LINE_REQ_DIR_OUT # type: ignore from spidev import SpiDev # type: ignore diff --git a/jacdac/transports/ws.py b/jacdac/transports/ws.py index a3e5078..81d349a 100644 --- a/jacdac/transports/ws.py +++ b/jacdac/transports/ws.py @@ -2,7 +2,7 @@ import threading from typing import Optional import websocket # type: ignore -from jacdac.bus import Transport +from jacdac.transport import Transport class WebSocketTransport(Transport): diff --git a/setup.py b/setup.py index 8544365..c787a7b 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,6 @@ setup( version=JD_VERSION, packages=find_packages( - include=['jacdac', 'jacdac.*'], exclude=['*test.py', 'jacdac.examples.*']) + include=['jacdac', 'jacdac.*'], exclude=['*test.py']) ) From e58dd9822088d7f6682512b2acf0e8465203de01 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Tue, 21 Jun 2022 15:48:37 -0700 Subject: [PATCH 4/6] fix is_connected --- jacdac/bus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jacdac/bus.py b/jacdac/bus.py index 7b399d4..e3c0f49 100644 --- a/jacdac/bus.py +++ b/jacdac/bus.py @@ -1679,7 +1679,7 @@ def packet_count(self): @ property def is_connected(self): - return len(self.clients) == 0 + return len(self.clients) != 0 @ property def short_id(self): From 0b3d46a785bc0fe4754f33a507d7076e6efeb75b Mon Sep 17 00:00:00 2001 From: pelikhan Date: Tue, 21 Jun 2022 15:50:52 -0700 Subject: [PATCH 5/6] removed pritns --- examples/blinky.py | 1 - jacdac/led/client.py | 15 ++++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/blinky.py b/examples/blinky.py index b14f46d..82b4a04 100644 --- a/examples/blinky.py +++ b/examples/blinky.py @@ -9,7 +9,6 @@ def main(): led.brightness = 0.5 # fade between colors while True: - print("blink") # blue led.set_all(0xff0000) sleep(1) diff --git a/jacdac/led/client.py b/jacdac/led/client.py index a90ece2..0c5728f 100644 --- a/jacdac/led/client.py +++ b/jacdac/led/client.py @@ -17,7 +17,6 @@ class LedClient(BufferClient): def __init__(self, bus: Bus, role: str) -> None: super().__init__(bus, JD_SERVICE_CLASS_LED, JD_LED_PACK_FORMATS, role) - @property def pixels(self) -> Optional[bytes]: """ @@ -29,7 +28,10 @@ def pixels(self) -> Optional[bytes]: @pixels.setter def pixels(self, value: bytes) -> None: - self.value + if value is None: + self.value = bytearray(0) + else: + self.value = bytearray(value) @property def brightness(self) -> Optional[float]: @@ -43,7 +45,6 @@ def brightness(self) -> Optional[float]: def brightness(self, value: float) -> None: self.register(JD_LED_REG_BRIGHTNESS).set_values(value / 100) - @property def actual_brightness(self) -> Optional[float]: """ @@ -78,7 +79,6 @@ def max_power(self) -> Optional[int]: def max_power(self, value: int) -> None: self.register(JD_LED_REG_MAX_POWER).set_values(value) - @property def leds_per_pixel(self) -> Optional[int]: """ @@ -122,18 +122,17 @@ def show(self): self._sync() self.refresh_value() - def set_all(self, rgb: int): """ Sets all the colors to particular color """ self._sync() - r= (rgb >> 16) & 0xff + r = (rgb >> 16) & 0xff g = (rgb >> 8) & 0xff b = (rgb >> 0) & 0xff buf = self.value dirty = self.dirty - for i in range(0,len(buf),3): + for i in range(0, len(buf), 3): dirty = dirty or buf[i] != r or buf[i + 1] != g or buf[i + 2] != b buf[i] = r buf[i + 1] = g @@ -141,5 +140,3 @@ def set_all(self, rgb: int): if dirty: self.set_dirty() self.show() - - From 64af9d09e854aca0b8f602c0cb7fac40375b2a1b Mon Sep 17 00:00:00 2001 From: pelikhan Date: Tue, 21 Jun 2022 15:54:51 -0700 Subject: [PATCH 6/6] fixing samples --- examples/slidy_blinky.py | 3 +-- examples/weather_to_csv.py | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/slidy_blinky.py b/examples/slidy_blinky.py index 1e7d135..ce41b68 100644 --- a/examples/slidy_blinky.py +++ b/examples/slidy_blinky.py @@ -12,9 +12,8 @@ def main(): # fade between colors while True: position = slider.position or 0. - brightness = position / 100. # change brightness - led.brightness = brightness + led.brightness = position sleep(1) main() diff --git a/examples/weather_to_csv.py b/examples/weather_to_csv.py index 0a52ccd..3ae87ae 100644 --- a/examples/weather_to_csv.py +++ b/examples/weather_to_csv.py @@ -1,6 +1,6 @@ from jacdac import Bus from jacdac.humidity import HumidityClient -from jacdac.thermometer import ThermometerClient +from jacdac.temperature import TemperatureClient import csv from time import sleep @@ -8,12 +8,12 @@ def main(): bus = Bus() humidity_sensor = HumidityClient(bus, "weather.hum") - thermometer = ThermometerClient(bus, "weather.temp") + temperature = TemperatureClient(bus, "weather.temp") with open('weather.csv', 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',', ) while True: h = humidity_sensor.humidity - t = thermometer.temperature + t = temperature.temperature writer.writerow([h, t]) sleep(1) main()