Skip to content

Commit

Permalink
Merge pull request #302 from krkeegan/Dynamic_Write_Time
Browse files Browse the repository at this point in the history
Change Next Write Time Passed to Serial or Hub to be Dynamic
  • Loading branch information
krkeegan authored Jan 5, 2021
2 parents 1f9aa4a + 90a2808 commit 2468e7c
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 30 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

- Fix error with backlight setting (thanks @tommycw1)([PR 299][P299])

- Small correction to write_time to allow dynamic calculation of time.
([PR 302][P302])

## [0.7.5]

This is another significant update that both improves the user experience,
Expand Down Expand Up @@ -554,3 +557,4 @@ will add new features.
[P285]: https://github.com/TD22057/insteon-mqtt/pull/285
[P299]: https://github.com/TD22057/insteon-mqtt/pull/299
[P303]: https://github.com/TD22057/insteon-mqtt/pull/303
[P302]: https://github.com/TD22057/insteon-mqtt/pull/302
14 changes: 13 additions & 1 deletion insteon_mqtt/Protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,18 @@ def set_wait_time(self, wait_time):
self._next_write_time).strftime('%H:%M:%S.%f')[:-3]
LOG.debug("Setting next write time: %s", print_time)

#-----------------------------------------------------------------------
def get_next_write_time(self):
"""Get the Timestamp of the next Permitted Write
Used by the Serial and Hub objects to determine when they can write a
message.
Returns:
(epoch Seconds): The next time a message can be written
"""
return self._next_write_time

#-----------------------------------------------------------------------
def is_addr_in_write_queue(self, addr):
"""Checks whether a message to the specified address already exists
Expand Down Expand Up @@ -546,7 +558,7 @@ def _send_next_msg(self):
# Write the message to the PLM modem. The message will only be sent
# when the current time is after the next write time as tracked by
# the link.
self.link.write(msg_bytes, self._next_write_time)
self.link.write(msg_bytes, self.get_next_write_time)
self._write_status = WriteStatus.PENDING_WRITE

#-----------------------------------------------------------------------
16 changes: 6 additions & 10 deletions insteon_mqtt/network/Hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,19 @@ def load_config(self, config):
assert self._password is not None

#-----------------------------------------------------------------------
def write(self, data, after_time=None):
def write(self, data, next_write_time):
"""Schedule data for writing to the serial device.
This pushes the data into a queue for writing to the Hub device.
The data is pushed to the HubClient in poll() and written during
the next HubClient loop.
Args:
after_time (float): Time in seconds past epoch after which to write
the packet. If None, the message will be sent whenever
it can.
next_write_time (function): A function that returns the timestamp
of the next permitted write time
"""
# Default after time is 0 which will always write.
after_time = after_time if after_time is not None else 0

# Save the input data to the write queue.
self._write_buf.append((data, after_time))
self._write_buf.append((data, next_write_time))

# if we have exceed the max queue size, pop the oldest packet off.
# This way if the link goes down for a long time, we don't just build
Expand Down Expand Up @@ -154,8 +150,8 @@ def _write_to_hub(self, t):

# Get the next data packet to write from the write queue and see if
# enough time has elapsed to write the message.
data, after_time = self._write_buf[0]
if t < after_time:
data, next_write_time = self._write_buf[0]
if t < next_write_time():
return

self.client.write(data)
Expand Down
20 changes: 8 additions & 12 deletions insteon_mqtt/network/Serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,19 @@ def fileno(self):
return self._fd

#-----------------------------------------------------------------------
def write(self, data, after_time=None):
def write(self, data, next_write_time):
"""Schedule data for writing to the serial device.
This pushes the data into a queue for writing to the serial device.
Only after the network event loop flags the device for writing will
it actually be written.
Args:
after_time (float): Time in seconds past epoch after which to write
the packet. If None, the message will be sent whenever
it can.
next_write_time (function): A function that returns the timestamp
of the next permitted write time
"""
# Default after time is 0 which will always write.
after_time = after_time if after_time is not None else 0

# Save the input data to the write queue.
self._write_buf.append((data, after_time))
self._write_buf.append((data, next_write_time))
self.signal_needs_write.emit(self, True)

# if we have exceed the max queue size, pop the oldest packet off.
Expand Down Expand Up @@ -206,9 +202,9 @@ def write_to_link(self, t):

# Get the next data packet to write from the write queue and see if
# enough time has elapsed to write the message.
data, after_time = self._write_buf[0]
if t < after_time:
#LOG.debug("Waiting to write %f < %f", t, after_time)
data, next_write_time = self._write_buf[0]
if t < next_write_time():
#LOG.debug("Waiting to write %f < %f", t, next_write_time())
return

try:
Expand Down Expand Up @@ -236,7 +232,7 @@ def write_to_link(self, t):
elif num:
# Still data to write - remove the written data from the
# buffer.
self._write_buf[0] = (data[num:], after_time)
self._write_buf[0] = (data[num:], next_write_time)

#-----------------------------------------------------------------------
def close(self):
Expand Down
3 changes: 2 additions & 1 deletion tests/message/test_Timed.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def test_send(self):
obj.send(protocol)

last = link.mock_calls[-1]
assert last == mock.call.write(msg.to_bytes(), 0)
assert last == mock.call.write(msg.to_bytes(),
protocol.get_next_write_time)

#-----------------------------------------------------------------------

Expand Down
6 changes: 4 additions & 2 deletions tests/network/test_Hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def test_read(self, test_hub, read, expected, calls):
#-----------------------------------------------------------------------
@pytest.mark.parametrize("write,t,expected,buffer,calls", [
(None, None, None, 0, 0),
(bytes([0x00]), None, bytes([0x00]), 0, 1),
(bytes([0x00]), time.time(), bytes([0x00]), 0, 1),
(bytes([0x00]), time.time() + 10, None, 1, 0),
])
def test_write(self, test_hub, write, t, expected, buffer, calls):
Expand All @@ -98,7 +98,9 @@ def test_write(self, test_hub, write, t, expected, buffer, calls):
test_hub.poll(time.time())
mock.patch.object(test_hub.client, 'write')
if write is not None:
test_hub.write(write, after_time=t)
def time_call():
return t
test_hub.write(write, time_call)
test_hub._write_to_hub(time.time())
args_list = test_hub.signal_wrote.emit.call_args_list
assert test_hub.signal_wrote.emit.call_count == calls
Expand Down
14 changes: 10 additions & 4 deletions tests/network/test_Serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,19 @@ def test_write_to_link_empty(self, test_device):

def test_write_to_link_too_soon(self, test_device):
t = time.time()
test_device._write_buf.append((bytes(8), time.time() + 5))
def call_time():
return time.time() + 5
test_device._write_buf.append((bytes(8), call_time))
with patch.object(test_device.signal_needs_write, 'emit') as mock_emit:
test_device.write_to_link(t)
mock_emit.assert_not_called()

def test_write_to_link_partial(self, test_device):
test_device.client.write_max = 4
msg_time = time.time()
test_device._write_buf.append((bytes(8), msg_time))
def call_time():
return msg_time
test_device._write_buf.append((bytes(8), call_time))
t = time.time()
with patch.object(test_device.signal_needs_write, 'emit') as needs_emit:
with patch.object(test_device.signal_wrote, 'emit') as wrote_emit:
Expand All @@ -66,7 +70,7 @@ def test_write_to_link_partial(self, test_device):
# Should still be 4 bytes in there
assert len(test_device._write_buf[0][0]) == 4
# After time should be the same
assert test_device._write_buf[0][1] == msg_time
assert test_device._write_buf[0][1] == call_time
# This should cause the rest of the data to be written
test_device.write_to_link(t)
needs_emit.assert_called_once_with(test_device, False)
Expand All @@ -78,7 +82,9 @@ def write(data):
raise Exception("Fake Serial", "Broken", "Test")
test_device.client.write = write
msg_time = time.time()
test_device._write_buf.append((bytes(8), msg_time))
def call_time():
return msg_time
test_device._write_buf.append((bytes(8), call_time))
t = time.time()
test_device.write_to_link(t)
assert "Serial write error" in caplog.text

0 comments on commit 2468e7c

Please sign in to comment.