started to refactor key pin and layer functionality
This commit is contained in:
@@ -1,4 +1,16 @@
|
||||
from micropython import const
|
||||
from nmlkpy import keymap_delta
|
||||
from nmlkpy.keymap_delta import KeymapDelta, LayerDelta
|
||||
from nmlkpy.pinstate_manager import PinState
|
||||
|
||||
|
||||
class KeyDelta:
|
||||
layer_delta: LayerDelta
|
||||
keymap_delta: KeymapDelta
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.layer_delta = LayerDelta()
|
||||
self.keymap_delta = KeymapDelta()
|
||||
|
||||
|
||||
class KeyEvent:
|
||||
@@ -42,8 +54,8 @@ class SoftRelease:
|
||||
def __init__(self):
|
||||
self._state = self._NOT_QUEUED
|
||||
|
||||
def is_not_queued(self) -> bool:
|
||||
return self._state == self._NOT_QUEUED
|
||||
def needs_attention(self) -> bool:
|
||||
return self._state != self._NOT_QUEUED
|
||||
|
||||
def is_queued(self) -> bool:
|
||||
return self._state == self._QUEUED
|
||||
@@ -93,6 +105,9 @@ class KeyBase:
|
||||
self.allowed_events = allowed_events
|
||||
self.events = KeyEvents()
|
||||
|
||||
def get_delta(self, pin: PinState) -> KeyDelta:
|
||||
raise NotImplementedError()
|
||||
|
||||
def _validate_next_event(self, next_event: int):
|
||||
if next_event is None or next_event not in self.allowed_events:
|
||||
raise Exception(
|
||||
@@ -105,11 +120,11 @@ class KeyBase:
|
||||
if next_event is KeyEvent.NO_EVENT:
|
||||
return False
|
||||
print(
|
||||
f"[{index}] next: {_event_to_string(next_event)}, current: {_event_to_string(self.events.current)}, previous: {_event_to_string(self.events.previous)}")
|
||||
f"[{index}] next: {_event_to_string(next_event)}, current: {_event_to_string(self.events.current)}, previous: {_event_to_string(self.events.previous)}, soft_release: {self.events.soft_release}")
|
||||
self.events.shift(next_event)
|
||||
return True
|
||||
|
||||
def handle(self, keyboard):
|
||||
def handle(self, keyboard, is_active: bool = False):
|
||||
raise NotImplementedError()
|
||||
|
||||
def self_test(self, keymap: list[list[object]]):
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .base import KeyBase, KeyEvent
|
||||
from .base import KeyBase, KeyDelta, KeyEvent
|
||||
from ..pin import PinEvent
|
||||
from ..pinstate_manager import PinState
|
||||
from ..keymap_delta import KeymapDelta
|
||||
|
||||
_VALID_KEYCODES = range(0, 255)
|
||||
_VALID_MODIFIERS = [0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000]
|
||||
@@ -8,6 +10,7 @@ _VALID_MODIFIERS = [0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000]
|
||||
class KeycodeBase(KeyBase):
|
||||
"""Basic press and release functionality that appends a keycode to the report when active"""
|
||||
keycode: int
|
||||
currently_pressed: bool
|
||||
|
||||
def __init__(self, keycode: int):
|
||||
allowed_events = [
|
||||
@@ -17,12 +20,32 @@ class KeycodeBase(KeyBase):
|
||||
]
|
||||
super().__init__(allowed_events)
|
||||
self.keycode = keycode
|
||||
self.currently_pressed = False
|
||||
|
||||
def self_test(self, keymap):
|
||||
raise NotImplementedError
|
||||
|
||||
def soft_release(self, keyboard):
|
||||
self.events.soft_release.queue()
|
||||
if self.currently_pressed:
|
||||
self.events.soft_release.queue()
|
||||
|
||||
def _press(self, keyboard):
|
||||
self.currently_pressed = True
|
||||
keyboard.add_keycode_to_report(self.keycode)
|
||||
|
||||
def _release(self, keyboard):
|
||||
self.currently_pressed = False
|
||||
keyboard.remove_keycode_from_report(self.keycode)
|
||||
|
||||
def _handle_soft_release(self, keyboard):
|
||||
if self.events.soft_release.is_queued():
|
||||
if self.currently_pressed:
|
||||
self._release(keyboard)
|
||||
self._set_event(KeyEvent.RELEASED)
|
||||
self.events.soft_release.reset()
|
||||
return
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def _consume_next_event(self, pin) -> bool:
|
||||
"""Returns: a boolean representing if the event property was updated with a new event or not"""
|
||||
@@ -47,21 +70,30 @@ class KeycodeBase(KeyBase):
|
||||
if self.events.soft_release.is_handled():
|
||||
pass
|
||||
if current == KeyEvent.PRESSED_:
|
||||
keyboard.add_keycode_to_report(self.keycode)
|
||||
self._press(keyboard)
|
||||
return
|
||||
if current == KeyEvent.RELEASED:
|
||||
keyboard.remove_keycode_from_report(self.keycode)
|
||||
return
|
||||
if current == KeyEvent.SOFT_RELEASE_HANDLED:
|
||||
keyboard.remove_keycode_from_report(self.keycode)
|
||||
self._release(keyboard)
|
||||
return
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def handle(self, keyboard, pin):
|
||||
if not self._consume_next_event(pin):
|
||||
def handle(self, keyboard, pin, is_active: bool = False):
|
||||
new_event_available = self._consume_next_event(pin)
|
||||
if self.events.soft_release.needs_attention():
|
||||
self._handle_soft_release(keyboard)
|
||||
return
|
||||
self._handle_event(keyboard)
|
||||
if new_event_available and is_active:
|
||||
self._handle_event(keyboard)
|
||||
|
||||
def get_delta(self, pin: PinState) -> KeyDelta:
|
||||
assert pin is not None
|
||||
changes = KeyDelta()
|
||||
if pin.is_pressed():
|
||||
changes.keymap_delta.press(self.keycode)
|
||||
else:
|
||||
changes.keymap_delta.unpress(self.keycode)
|
||||
return changes
|
||||
|
||||
|
||||
class Keycode(KeycodeBase):
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from .base import KeyEvent, KeyBase
|
||||
from ..keymap_delta import KeymapDelta, LayerChange
|
||||
from ..pinstate_manager import PinState
|
||||
from .base import KeyDelta, KeyEvent, KeyBase
|
||||
from ..pin import PinEvent, Pin
|
||||
|
||||
|
||||
@@ -39,9 +41,28 @@ class Toggle(LayerKeyBase):
|
||||
]
|
||||
super().__init__(layer, allowed_events)
|
||||
|
||||
def _press(self, keyboard):
|
||||
keyboard.layer.toggle(self.layer_to_switch_to)
|
||||
self._release_all_other_keys(keyboard)
|
||||
|
||||
def _release(self, keyboard):
|
||||
keyboard.layer.release_toggled()
|
||||
self._release_all_other_keys(keyboard)
|
||||
|
||||
def soft_release(self, keyboard):
|
||||
self.events.soft_release.queue()
|
||||
|
||||
def _handle_soft_release(self, keyboard):
|
||||
if self.events.soft_release.is_queued():
|
||||
# leads to recursion
|
||||
self._release(keyboard)
|
||||
self.events.soft_release.mark_as_handled()
|
||||
return
|
||||
if self.events.soft_release.is_handled():
|
||||
self.events.soft_release.reset()
|
||||
return
|
||||
raise NotImplementedError()
|
||||
|
||||
def _consume_next_event(self, pin: Pin) -> bool:
|
||||
"""Returns: a boolean representing if the event property was updated with a new event or not"""
|
||||
next_pin_event = pin.read_event()
|
||||
@@ -88,13 +109,17 @@ class Toggle(LayerKeyBase):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def handle(self, keyboard, pin: Pin):
|
||||
if not self._consume_next_event(pin):
|
||||
def handle(self, keyboard, pin: Pin, is_active: bool = False):
|
||||
if self.events.soft_release.needs_attention():
|
||||
self._handle_soft_release(keyboard)
|
||||
return
|
||||
self._handle_event(keyboard, pin)
|
||||
if self._consume_next_event(pin):
|
||||
self._handle_event(keyboard, pin)
|
||||
|
||||
|
||||
class Hold(LayerKeyBase):
|
||||
currently_pressed: bool
|
||||
|
||||
def __init__(self, layer: int):
|
||||
allowed_events = [
|
||||
KeyEvent.NO_EVENT,
|
||||
@@ -102,14 +127,28 @@ class Hold(LayerKeyBase):
|
||||
KeyEvent.RELEASED
|
||||
]
|
||||
super().__init__(layer, allowed_events)
|
||||
self.currently_pressed = False
|
||||
|
||||
def soft_release(self, keyboard):
|
||||
self.events.soft_release.queue()
|
||||
if self.currently_pressed:
|
||||
self.events.soft_release.queue()
|
||||
|
||||
def handle(self, keyboard, pin):
|
||||
if not self._consume_next_event(pin):
|
||||
def _handle_soft_release(self, keyboard):
|
||||
if self.events.soft_release.is_queued():
|
||||
if self.currently_pressed:
|
||||
self._release(keyboard)
|
||||
self._set_event(KeyEvent.RELEASED)
|
||||
self.events.soft_release.reset()
|
||||
return
|
||||
self._handle_event(keyboard)
|
||||
raise NotImplementedError()
|
||||
|
||||
def handle(self, keyboard, pin, is_active: bool = False):
|
||||
new_event_available = self._consume_next_event(pin)
|
||||
if self.events.soft_release.needs_attention():
|
||||
self._handle_soft_release(keyboard)
|
||||
return
|
||||
if new_event_available and is_active:
|
||||
self._handle_event(keyboard)
|
||||
|
||||
def _consume_next_event(self, pin: Pin) -> bool:
|
||||
next_pin_event = pin.read_event()
|
||||
@@ -129,12 +168,30 @@ class Hold(LayerKeyBase):
|
||||
current = self.events.current
|
||||
|
||||
if current == KeyEvent.PRESSED_:
|
||||
keyboard.layer.hold(self.layer_to_switch_to)
|
||||
self._release_all_other_non_toggle_keys(keyboard)
|
||||
self._press(keyboard)
|
||||
return
|
||||
if current == KeyEvent.RELEASED:
|
||||
keyboard.layer.release_held()
|
||||
self._release_all_other_non_toggle_keys(keyboard)
|
||||
self._release(keyboard)
|
||||
return
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def _press(self, keyboard):
|
||||
self.currently_pressed = True
|
||||
keyboard.layer.hold(self.layer_to_switch_to)
|
||||
self._release_all_other_non_toggle_keys(keyboard)
|
||||
|
||||
def _release(self, keyboard):
|
||||
self.currently_pressed = False
|
||||
keyboard.layer.release_held()
|
||||
self._release_all_other_non_toggle_keys(keyboard)
|
||||
|
||||
def get_delta(self, pin: PinState) -> KeyDelta:
|
||||
changes = KeyDelta()
|
||||
if pin.is_pressed():
|
||||
changes.layer_delta.apply_layer(
|
||||
LayerChange(self.layer_to_switch_to))
|
||||
else:
|
||||
changes.layer_delta.remove_layer(
|
||||
LayerChange(self.layer_to_switch_to))
|
||||
return changes
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
from micropython import const
|
||||
from adafruit_mcp230xx.mcp23017 import MCP23017
|
||||
from nmlkpy.keymap_manager import KeymapManager
|
||||
from .tests import test_keymap
|
||||
from .key_types.base import KeyBase
|
||||
from .pin import Pin
|
||||
@@ -204,14 +205,20 @@ class Keyboard:
|
||||
def _initialize_session_values(self):
|
||||
self.toggled_layer = 0
|
||||
self.held_layer = None
|
||||
self.pressed_keys = set()
|
||||
self.pressed_keys_last_cycle = set()
|
||||
self.pressed_keys = []
|
||||
self.pressed_keys_last_cycle = []
|
||||
|
||||
def get_key(self, layer_index, key_index) -> KeyBase:
|
||||
def get_active_key(self, layer_index, key_index) -> KeyBase:
|
||||
return self.keymap[layer_index][key_index]
|
||||
|
||||
def get_inactive_keys(self, active_layer_index, key_index) -> list[KeyBase]:
|
||||
layers_excluding_active = list(
|
||||
range(len(self.keymap)))
|
||||
layers_excluding_active.remove(active_layer_index)
|
||||
return [self.keymap[layer][key_index] for layer in layers_excluding_active]
|
||||
|
||||
def add_keycode_to_report(self, keycode: int):
|
||||
self.pressed_keys.add(keycode)
|
||||
self.pressed_keys.append(keycode)
|
||||
|
||||
def remove_keycode_from_report(self, keycode: int):
|
||||
try:
|
||||
@@ -251,15 +258,17 @@ class Keyboard:
|
||||
self._initialize_session_values()
|
||||
if self.debug_repl:
|
||||
print("Keyboard started")
|
||||
m = KeymapManager(self.keymap, self.pins)
|
||||
while True:
|
||||
for pin in self.pins:
|
||||
key = self.get_key(self.layer.get(), pin.index)
|
||||
key.handle(self, pin)
|
||||
self._update_layer_led()
|
||||
|
||||
if self.pressed_keys != self.pressed_keys_last_cycle:
|
||||
keymap_delta = m.step()
|
||||
if keymap_delta.contains_keycode_changes():
|
||||
for keycode in keymap_delta.get_to_press():
|
||||
self.add_keycode_to_report(keycode)
|
||||
for keycode in keymap_delta.get_to_unpress():
|
||||
self.remove_keycode_from_report(keycode)
|
||||
self.send_nkro_report()
|
||||
self.pressed_keys_last_cycle = set(self.pressed_keys)
|
||||
|
||||
self._update_layer_led()
|
||||
except Exception as e:
|
||||
if self.debug_repl:
|
||||
raise e
|
||||
|
||||
86
nmlkpy/keymap_delta.py
Normal file
86
nmlkpy/keymap_delta.py
Normal file
@@ -0,0 +1,86 @@
|
||||
class KeyIdentifier:
|
||||
_layer_index: int
|
||||
_key_index: int
|
||||
|
||||
def __init__(self, layer_index: int, key_index: int) -> None:
|
||||
assert layer_index is not None
|
||||
assert key_index is not None
|
||||
self._layer_index = layer_index
|
||||
self._key_index = key_index
|
||||
|
||||
|
||||
class LayerChange:
|
||||
_issued_by: KeyIdentifier
|
||||
_to_layer: int
|
||||
_is_toggle: bool
|
||||
|
||||
def __init__(self, to_layer: int, is_toggle: bool = False) -> None:
|
||||
assert to_layer is not None
|
||||
self._issued_by = None
|
||||
self._to_layer = to_layer
|
||||
self._is_toggle = is_toggle
|
||||
|
||||
def get_layer(self) -> int:
|
||||
return self._to_layer
|
||||
|
||||
|
||||
class LayerDelta:
|
||||
_layers_to_apply: list[LayerChange]
|
||||
_layers_to_remove: list[LayerChange]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._layers_to_remove = []
|
||||
self._layers_to_apply = []
|
||||
|
||||
def contains_layer_change(self) -> bool:
|
||||
return len(self._layers_to_apply) + len(self._layers_to_remove) > 0
|
||||
|
||||
def get_layers_to_apply(self) -> list[LayerChange]:
|
||||
return self._layers_to_apply
|
||||
|
||||
def get_layers_to_remove(self) -> list[LayerChange]:
|
||||
return self._layers_to_remove
|
||||
|
||||
def apply_layer(self, layer_change: LayerChange) -> None:
|
||||
assert layer_change is not None
|
||||
self._layers_to_apply.append(layer_change)
|
||||
|
||||
def remove_layer(self, layer_change: LayerChange) -> None:
|
||||
assert layer_change is not None
|
||||
self._layers_to_remove.append(layer_change)
|
||||
|
||||
def merge_deltas(self, other_delta) -> None:
|
||||
for layer in other_delta.get_layers_to_apply():
|
||||
self.apply_layer(layer)
|
||||
for layer in other_delta.get_layers_to_remove():
|
||||
self.remove_layer(layer)
|
||||
|
||||
|
||||
class KeymapDelta:
|
||||
_keycodes_to_press: list[int]
|
||||
_keycodes_to_unpress: list[int]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._keycodes_to_press = []
|
||||
self._keycodes_to_unpress = []
|
||||
|
||||
def get_to_press(self) -> list[int]:
|
||||
return self._keycodes_to_press
|
||||
|
||||
def get_to_unpress(self) -> list[int]:
|
||||
return self._keycodes_to_unpress
|
||||
|
||||
def contains_keycode_changes(self) -> bool:
|
||||
return len(self._keycodes_to_unpress) + len(self._keycodes_to_press) > 0
|
||||
|
||||
def press(self, keycode: int) -> None:
|
||||
self._keycodes_to_press.append(keycode)
|
||||
|
||||
def unpress(self, keycode: int) -> None:
|
||||
self._keycodes_to_unpress.append(keycode)
|
||||
|
||||
def merge_deltas(self, other_delta) -> None:
|
||||
for keycode in other_delta.get_to_press():
|
||||
self.press(keycode)
|
||||
for keycode in other_delta.get_to_unpress():
|
||||
self.unpress(keycode)
|
||||
85
nmlkpy/keymap_manager.py
Normal file
85
nmlkpy/keymap_manager.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from nmlkpy.key_types.layer import Hold
|
||||
from .keymap_delta import KeymapDelta, LayerChange, LayerDelta
|
||||
from .pinstate_manager import PinState, PinManager
|
||||
from .key_types.base import KeyBase
|
||||
from .key_types.keycode import KeycodeBase
|
||||
from .pin import Pin
|
||||
from micropython import const
|
||||
|
||||
from nmlkpy import keymap_delta
|
||||
|
||||
|
||||
# Needs to be built in a way where every "key type" only has to know how to modify it's own state
|
||||
# it cannot be passed a pin or keyboard instance
|
||||
# this is to help keep order in what can access what
|
||||
|
||||
# layer manager should be able to figure out what needs to happen to the state after a loop is run.
|
||||
#
|
||||
|
||||
# Keys
|
||||
# Keys have one way communication from KeymapManager to key and are allowed to return a result
|
||||
# all keys changes together create a delta that is handled by the manager
|
||||
# the manager defines an interface for changes that can happen to the state
|
||||
# - Pressed
|
||||
# - unpressed
|
||||
# - Layer was changed
|
||||
# When a delta is applied the manager can figure out what to do for that state index
|
||||
|
||||
# state
|
||||
# Keys state is stored and managed in the manager
|
||||
# One single layer of state removes the issue of when layers are changing
|
||||
# each state is a class that can be consumed from different aspects, keycode, toggle, hold etc
|
||||
# Where the state presents itself in the way that is asked
|
||||
|
||||
|
||||
# Pins
|
||||
# Can be asked for their current event
|
||||
# Pins state is a class that holds current and last, can give you the delta to figure out what to change
|
||||
_LAYERS_NONE = 0
|
||||
|
||||
|
||||
class LayerManager:
|
||||
number_of_layers: int = None
|
||||
current_layer: int = None
|
||||
|
||||
def __init__(self, number_of_layers: int) -> None:
|
||||
assert number_of_layers is not None
|
||||
assert number_of_layers > 0
|
||||
self.number_of_layers = number_of_layers
|
||||
self.current_layer = _LAYERS_NONE
|
||||
|
||||
def get_current_layer(self) -> int:
|
||||
return self.current_layer
|
||||
|
||||
def handle_layer_changes(self, layer):
|
||||
pass
|
||||
|
||||
|
||||
class KeymapManager:
|
||||
keymap: list[list[KeyBase]] = None
|
||||
pin_manager: PinManager = None
|
||||
layer_manager: LayerManager = None
|
||||
|
||||
def __init__(self, keymap: list[list[KeyBase]], pins: list[Pin]):
|
||||
self.keymap = keymap
|
||||
self.pin_manager = PinManager(pins)
|
||||
self.layer_manager = LayerManager(len(keymap))
|
||||
|
||||
def step(self) -> KeymapDelta:
|
||||
self.pin_manager.step()
|
||||
pin_changes = self.pin_manager.get_state_delta()
|
||||
keymap_changes = KeymapDelta()
|
||||
layer_changes = LayerDelta()
|
||||
layer_index = self.layer_manager.get_current_layer()
|
||||
for pin_index in range(len(pin_changes)):
|
||||
pin_state = pin_changes[pin_index]
|
||||
if pin_state is None:
|
||||
continue
|
||||
|
||||
key = self.keymap[layer_index][pin_index]
|
||||
if isinstance(key, KeycodeBase) or isinstance(key, Hold):
|
||||
key_changes = key.get_delta(pin_state)
|
||||
keymap_changes.merge_deltas(key_changes.keymap_delta)
|
||||
layer_changes.merge_deltas(key_changes.layer_delta)
|
||||
|
||||
return keymap_changes
|
||||
@@ -25,7 +25,6 @@ class PinStates:
|
||||
class Pin:
|
||||
index: int
|
||||
hw_pin: digitalio.DigitalInOut
|
||||
previous_state: int
|
||||
|
||||
def __init__(self, index, hw_pin: digitalio.DigitalInOut):
|
||||
self.index = index
|
||||
@@ -38,15 +37,7 @@ class Pin:
|
||||
return states
|
||||
|
||||
def read_event(self) -> int:
|
||||
states = self._consume_next_state()
|
||||
if states.previous == PinState.RELEASED:
|
||||
if states.current == PinState.RELEASED:
|
||||
return PinEvent.NO_EVENT
|
||||
if states.current == PinState.PRESSED:
|
||||
return PinEvent.PRESSED
|
||||
if states.previous == PinState.PRESSED:
|
||||
if states.current == PinState.PRESSED:
|
||||
return PinEvent.NO_EVENT
|
||||
if states.current == PinState.RELEASED:
|
||||
return PinEvent.RELEASED
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_value(self) -> int:
|
||||
return self.hw_pin.value
|
||||
|
||||
50
nmlkpy/pinstate_manager.py
Normal file
50
nmlkpy/pinstate_manager.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from micropython import const
|
||||
from .pin import Pin
|
||||
|
||||
_PINSTATE_PRESSED = const(0)
|
||||
_PINSTATE_UNPRESSED = const(1)
|
||||
|
||||
|
||||
class PinState:
|
||||
_state: int
|
||||
|
||||
def __init__(self, pin_value: int) -> None:
|
||||
self._state = _PINSTATE_PRESSED if pin_value == _PINSTATE_PRESSED else _PINSTATE_UNPRESSED
|
||||
|
||||
def __eq__(self, obj):
|
||||
return isinstance(obj, PinState) and self._state == obj._state
|
||||
|
||||
def __ne__(self, obj):
|
||||
return not isinstance(obj, PinState) or self._state != obj._state
|
||||
|
||||
def is_pressed(self) -> bool:
|
||||
return self._state == _PINSTATE_PRESSED
|
||||
|
||||
|
||||
class PinManager:
|
||||
|
||||
_pins: list[Pin] = []
|
||||
_current_state: list[PinState] = []
|
||||
_previous_state: list[PinState] = []
|
||||
|
||||
def __init__(self, pins: list[Pin]) -> None:
|
||||
self._pins = pins
|
||||
self.step()
|
||||
self.step()
|
||||
|
||||
def _get_next_state(self) -> list[PinState]:
|
||||
return [PinState(pin.get_value()) for pin in self._pins]
|
||||
|
||||
def step(self) -> None:
|
||||
self._previous_state = self._current_state
|
||||
self._current_state = self._get_next_state()
|
||||
|
||||
def get_state_delta(self) -> list[PinState]:
|
||||
"""Compares the current state with the previous and returns a list of either PinState or None depending on if the state changed or not"""
|
||||
delta = []
|
||||
for i in range(len(self._pins)):
|
||||
previous_state = self._previous_state[i]
|
||||
current_state = self._current_state[i]
|
||||
delta.append(current_state if current_state !=
|
||||
previous_state else None)
|
||||
return delta
|
||||
Reference in New Issue
Block a user