Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change Next Write Time Passed to Serial or Hub to be Dynamic #302

Merged
merged 3 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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