Skip to content
Draft
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
2 changes: 1 addition & 1 deletion pstop_c/examples/machine/machine_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ main(int argc, char *argv[])
pstop_application_set_log_cb(&pstop_app, simple_log);
pstop_application_set_remote_cb(&pstop_app, is_operator_allowed);
pstop_application_set_hardware_status_cb(&pstop_app, robot_status);

pstop_application_set_protocol_limits(&pstop_app, 1U, 2U, 1000U);
machine_init(&machine, &pstop_app, pstop_clients, MAX_CLIENTS);

int port = 8890;
Expand Down
2 changes: 1 addition & 1 deletion pstop_c/pstop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ add_executable(pstop_requirements_test
test/src/pstop/requirements/req_2_01_test.c
test/src/pstop/requirements/req_2_02_test.c
test/src/pstop/requirements/req_2_03_test.c
test/src/pstop/requirements/req_2_04_test.c

test/src/pstop/requirements/req_2_06_test.c
test/src/pstop/requirements/req_2_07_test.c
Expand All @@ -97,7 +98,6 @@ add_executable(pstop_requirements_test
test/src/pstop/requirements/req_3_19_test.c
test/src/pstop/requirements/req_3_20_test.c
test/src/pstop/requirements/req_3_21_test.c

test/src/pstop/requirements/req_3_22_test.c
test/src/pstop/requirements/req_3_23_test.c

Expand Down
51 changes: 37 additions & 14 deletions pstop_c/pstop/src/pstop/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

#include "pstop/protocol.h"

static
uint64_t
get_diff(uint64_t lhs, uint64_t rhs)
{
if(lhs <= rhs) {
return rhs - lhs;
}

return lhs - rhs;
}

static
int
is_checksum_valid(const pstop_msg_t *req)
Expand Down Expand Up @@ -35,28 +46,40 @@ check_counter(const pstop_application_config_t *app_config, const pstop_remote_d

static
pstop_error_t
check_timestamp(const pstop_application_config_t *app_config, const protocol_data_t *client, const pstop_msg_t *req)
check_timestamp(const pstop_application_t *app, const protocol_data_t *client, const pstop_msg_t *req)
{
if(req->stamp <= client->last_received_stamp) {
return PSTOP_MSG_OUT_OF_ORDER;
}

if(req->received_stamp == client->last_timestamp) {
return PSTOP_OK;
}

// did the other end miss a message?
if(req->received_stamp < client->last_timestamp) {
uint64_t diff = client->last_timestamp - req->received_stamp;
// example of missed messages
// last sent stamp = 500, last received stamp = 500
// now = 600, current request stamp = 900
// diff = 100, remote diff = 400
// diff should be close to the same if no messages have been lost

uint16_t missed = (uint16_t)(diff / client->heartbeat_ms);
uint64_t diff_received_stamp = get_diff(req->stamp, client->last_received_stamp);

if(missed >= (app_config->max_missed_heartbeats + 1U)) {
return PSTOP_MSG_LOST;
}
uint64_t now = app->env.get_time_cb();

uint64_t diff_sent_stamp = get_diff(now, client->last_timestamp);

uint64_t diff_remote_vs_local = get_diff(diff_sent_stamp, diff_received_stamp);

if(diff_sent_stamp == 0U) {
// the chances of this happening are extremely unlikely.
// It would mean that the amount of time to run through processing
// a single message takes 0ms.

// If this does happen then the received timestamps should also be
// close to 0ms. Setting diff_sent_stamp = 1 will mean that the
// diff_remote_vs_local is the max number of missed hearbeats.
diff_sent_stamp = 1U;
}
else {
return PSTOP_MSG_OUT_OF_ORDER;
uint64_t missed = diff_remote_vs_local / diff_sent_stamp;
Comment on lines +62 to +80

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 This regresses the missed-heartbeat detector: missed is diff_remote_vs_local/diff_sent_stamp, where diff_sent_stamp is the machine's own arrival-time gap (protocol.c:66,80), not the base's heartbeat_ms/round-trip-echo count. A late-arriving prior message shrinks diff_sent_stamp, so missed balloons and PSTOP_MSG_LOST fires though nothing was lost. If delay stays constant while a real heartbeat is skipped, both deltas grow together and missed stays near 0, masking real loss. Fix: derive missed from a value immune to arrival jitter, e.g. heartbeat_ms or the round-trip echo the base used. [also at: pstop_c/pstop/src/pstop/protocol.c:81 - Machine operators now get spurious PSTOP_MSG_LOST stops after ordinary network jitter/delay recovery, even though no message was actually lost. diff_sent_stamp (line 66) is the real wall-clock gap between successive message PROCESSING times, not a fixed interval; a burst of quick arrivals right…]

Extended reasoning...

protocol_handle_message updates client->remote_data.last_timestamp=now (machine clock) and last_received_stamp=req->stamp (remote clock) on every processed message. check_timestamp (protocol.c:62-81) compares diff_received_stamp=|req->stamp-last_received_stamp| (remote clock delta) to diff_sent_stamp=|now-last_timestamp| (machine clock delta) and flags LOST when their difference/diff_sent_stamp exceeds max_missed_heartbeats+1. Example, heartbeat_ms=100: message N-1 is network-delayed 90ms so it's processed late; message N arrives on schedule 10ms after that. diff_sent_stamp=10, diff_received_stamp=100 (unaffected by transport delay, it's remote send-time gap). missed=90/10=9>threshold, LOST fires though nothing was lost. Reverse: remote genuinely skips one send but transport delay stays constant, so both deltas grow by the same skipped interval and missed stays ~0 — real loss goes undetected. Base algorithm compared only machine-clock-domain values…

Verification: normal. The new detector (protocol.c:62-82) computes missed = get_diff(diff_sent_stamp, diff_received_stamp) / diff_sent_stamp, with diff_sent_stamp = |now - last_timestamp| (line 64-66) being the machine's own inter-arrival gap (last_timestamp is set to now per processed message, line 159) and diff_received_stamp = |req->stamp - last_received_stamp| (line 62) the remote-clock gap. The…

if(missed > (uint64_t)(app->app_config.max_missed_heartbeats + 1U)) {
return PSTOP_MSG_LOST;
Comment on lines +49 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 This PR edits pstop_c protocol/timestamp logic directly in this repo, which CLAUDE.md designates off-limits: pstop_c is pre-qualified, "never edited here, never re-verified"; protocol and machine-safety changes must go upstream and return as a version bump. check_timestamp's signature and missed-heartbeat arithmetic (protocol.c:49-75) and pstop_application_init's default limits (pstop_application.c:47) are exactly the counter/stamp/heartbeat-timeout logic the manual reserves for the upstream track, and per CLAUDE.md this is also automatic Class C (touches timing/heartbeat-timeout on the stop path) requiring two-person sign-off before implementation, not present here. …

Extended reasoning...

…Fix: revert this change from pstop_c/ in this repo and land it upstream as a version bump with Class C review, per CLAUDE.md.

CLAUDE.md states 'pstop_c/ is off-limits. Pre-qualified, separate upstream track. Referenced at the interface, never edited here, never re-verified. Protocol and machine-safety changes go upstream and return as a version bump.' This diff modifies pstop_c/pstop/src/pstop/protocol.c (check_timestamp signature and body) and pstop_c/pstop/src/pstop/pstop_application.c (default protocol limits), both inside pstop_c/. Any change touching counter/stamp/heartbeat-timeout logic is automatic Class C requiring two-person authorization before implementation; nothing in the diff or PR description shows that sign-off. Landing this as-is bypasses the pre-qualification boundary that keeps the safety case for the missed-heartbeat diagnostic valid; any downstream user pulling this repo now gets an unvetted, locally-modified version of pre-qualified safety code instead of the upstream-verified one.

Verification: normal — the diff directly edits pstop_c/, which CLAUDE.md §1.2 declares off-limits ("Pre-qualified, separate upstream track. Referenced at the interface, never edited here, never re-verified. Protocol and machine-safety changes go upstream and return as a version bump"). The violation is not hypothetical; it is the changeset itself. Confirmed in the diff: pstop_c/pstop/src/pstop/protocol.c…

}

return PSTOP_OK;
Comment on lines +49 to 85

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 check_timestamp() no longer validates req->received_stamp (the remote's echo of our last sent stamp) after bonding, unlike check_counter() which still checks req->received_counter against msg_counter. A corrupted or forged received_stamp on the untrusted black channel is no longer rejected as PSTOP_MSG_OUT_OF_ORDER as long as req->stamp keeps advancing and the new heartbeat ratio stays under threshold. Fix: keep validating that req->received_stamp bounds/echoes client->last_timestamp (mirroring the received_counter check in check_counter), in addition to the new missed-heartbeat ratio math, so black-channel corruption of the echoed stamp is still caught for every message, not only at bond time (line 125).

Extended reasoning...

Base check_timestamp used req->received_stamp: if(req->received_stamp == client->last_timestamp) return OK; and rejected received_stamp > last_timestamp as OUT_OF_ORDER, catching an echo that could not legitimately have been sent yet. New check_timestamp (protocol.c:49-86) never reads req->received_stamp; it only compares req->stamp to last_received_stamp (line 51) and computes diff_received_stamp from req->stamp, not req->received_stamp (line 62). protocol_handle_message (line 118) is the only caller for bonded clients and does no other received_stamp check. So an attacker/corruption on the black channel can send any req->received_stamp (e.g. stale/garbage) every message; as long as req->stamp increases and roughly tracks time, the message passes check_counter and check_timestamp. check_counter (lines 37-39) still validates req->received_counter > msg_counter as OUT_OF_ORDER, so the asymmetry is new. No test in protocol_test.c or req_2_04_test.c sets an invalid received_stamp; all set it to the prior resp.stamp, so the regression is untested.

Verification: nit. The candidate reads the code correctly and the scenario is reachable. Base check_timestamp (git show 9d3b0e4:protocol.c) rejected req->received_stamp > client->last_timestamp as PSTOP_MSG_OUT_OF_ORDER (the else branch after the == and < cases). New check_timestamp (protocol.c:49-86) never references req->received_stamp at all — it reads only req->stamp (lines 51, 62) and the machine…

Expand Down Expand Up @@ -92,7 +115,7 @@ protocol_handle_message(pstop_machine_t *machine, const pstop_msg_t *req, pstop_
return err;
}

err = check_timestamp(&(machine->application->app_config), &(client->remote_data), req);
err = check_timestamp(machine->application, &(client->remote_data), req);
if(err != PSTOP_OK) {
return err;
}
Expand Down
2 changes: 1 addition & 1 deletion pstop_c/pstop/src/pstop/pstop_application.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pstop_application_init(pstop_application_t *app)
pstop_application_set_remote_cb(app, NULL);
pstop_application_set_hardware_status_cb(app, NULL);
pstop_application_set_log_cb(app, NULL);
pstop_application_set_protocol_limits(app, 0U, 0U, 1000U);
pstop_application_set_protocol_limits(app, 1U, 1U, 1000U);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) Any downstream app that skips pstop_application_set_protocol_limits() now silently tolerates one lost STOP/OK message on the counter path, not just heartbeats, weakening default loss detection versus the base branch. pstop_application_init() changed the default max_lost_messages from 0 to 1; check_counter() in protocol.c:34 flags PSTOP_MSG_LOST only when counter gap exceeds max_lost_messages+1, so a gap of 2 (one message actually lost) now passes silently where it used to trip immediately. This bump is unrelated to the timestamp/heartbeat rework the PR is about and has no accompanying test coverage or migration note for the counter axis. …

Extended reasoning...

…Fix: keep max_lost_messages at 0 by default (or otherwise preserve zero-tolerance counter-loss detection) and bump only max_missed_heartbeats, since the PR's stated purpose is heartbeat timing, not counter-gap tolerance.

protocol.c check_counter(): line34 if((req->counter - client->remote_data.last_received_counter) > (app_config->max_lost_messages + 1U)) return PSTOP_MSG_LOST;. Base default was max_lost_messages=0, so threshold is >1: a counter jump of 2 (i.e. exactly one message dropped on the wire) triggers LOST right away. pstop_application.c:47 now calls pstop_application_set_protocol_limits(app,1U,1U,1000U) for every app using the default init path, so threshold becomes >2: a jump of 2 (one lost message) no longer triggers LOST; only losing 2+ messages does. Same widened tolerance applies to the received_counter path (protocol.c:40, >= max_lost_messages+1U). No test in this diff exercises the default-init path for the counter axis; machine_test.c and the requirements tests all call pstop_application_set_protocol_limits explicitly with their own values, so this…

Verification: nit. The mechanism is real and reachable. pstop_application.c:47 changes the default from pstop_application_set_protocol_limits(app, 0U, 0U, 1000U) to (app, 1U, 1U, 1000U). protocol.c:34 (check_counter, untouched by the diff) trips PSTOP_MSG_LOST only when (req->counter - client->remote_data.last_received_counter) > (app_config->max_lost_messages + 1U). Base default max_lost_messages=0…

}

void
Expand Down
16 changes: 13 additions & 3 deletions pstop_c/pstop/test/src/pstop/protocol_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ test_protocol_bond_then_unbond(void)
machine_init(&machine, &pstop_app, pstop_clients, MAX_CLIENTS);

operator_allowed_flag = true;
current_time = 10;
current_time = 100;

pstop_msg_t req;
pstop_message_init(&req);
Expand All @@ -212,7 +212,7 @@ test_protocol_bond_then_unbond(void)
TEST_ASSERT_EQUAL(1U, resp.counter);
TEST_ASSERT_EQUAL(10, resp.received_counter);
TEST_ASSERT_EQUAL(100, resp.received_stamp);
TEST_ASSERT_EQUAL(12, resp.stamp);
TEST_ASSERT_EQUAL(102, resp.stamp);
TEST_ASSERT_EQUAL(0, device_id_cmp(&req.id, &resp.receiver_id));
TEST_ASSERT_EQUAL(0, device_id_cmp(&req.receiver_id, &resp.id));

Expand All @@ -223,7 +223,8 @@ test_protocol_bond_then_unbond(void)
req.id.data = PSTOP_ID;
req.receiver_id.data = MACHINE_ID;
req.received_counter = 1;
req.received_stamp = 12;
req.received_stamp = 100;
current_time = 110;
pstop_message_init(&resp);
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
TEST_ASSERT_EQUAL(PSTOP_MESSAGE_UNBOND, resp.message);
Expand Down Expand Up @@ -382,6 +383,7 @@ test_protocol_bond_correct_timestamp(void)
req.received_stamp = resp.stamp;
req.received_counter = resp.counter;
req.stamp = 110;
current_time = 110;
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
}

Expand Down Expand Up @@ -479,12 +481,16 @@ test_protocol_bond_invalid_echo_counter(void)
req.message = PSTOP_MESSAGE_OK;
req.counter = 11;
req.stamp = 110;
req.received_stamp = 100;
req.received_counter = 1;
current_time = 110;
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));

req.message = PSTOP_MESSAGE_OK;
req.counter = 12;
req.stamp = 120;
req.received_stamp = 110;
current_time = 120;
req.received_counter = 0;
TEST_ASSERT_EQUAL(PSTOP_MSG_LOST, machine_process_message(&machine, &req, &resp));
}
Expand Down Expand Up @@ -519,6 +525,8 @@ test_protocol_bond_missing_sent_messages(void)
req.counter = 11;
req.stamp = 110;
req.received_counter = 0; // we didn't receive the previous mssage
req.received_stamp = 100;
current_time = 110;
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
TEST_ASSERT_EQUAL(2U, resp.counter);
TEST_ASSERT_EQUAL(PSTOP_MESSAGE_STOP, resp.message);
Expand All @@ -527,6 +535,8 @@ test_protocol_bond_missing_sent_messages(void)
req.counter = 12;
req.stamp = 120;
req.received_counter = 0; // we didn't receive the previous mssage
req.received_stamp = 110;
current_time = 120;
TEST_ASSERT_EQUAL(PSTOP_MSG_LOST, machine_process_message(&machine, &req, &resp));
TEST_ASSERT_EQUAL(2U, resp.counter);
TEST_ASSERT_EQUAL(PSTOP_MESSAGE_STOP, resp.message);
Expand Down
4 changes: 2 additions & 2 deletions pstop_c/pstop/test/src/pstop/requirements/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
extern void main_req_2_01_test(void);
extern void main_req_2_02_test(void);
extern void main_req_2_03_test(void);
extern void main_req_2_04_test(void);

extern void main_req_2_06_test(void);
extern void main_req_2_07_test(void);
Expand All @@ -33,7 +34,6 @@ extern void main_req_3_18_test(void);
extern void main_req_3_19_test(void);
extern void main_req_3_20_test(void);
extern void main_req_3_21_test(void);

extern void main_req_3_22_test(void);
extern void main_req_3_23_test(void);

Expand All @@ -49,6 +49,7 @@ main(void)
main_req_2_01_test();
main_req_2_02_test();
main_req_2_03_test();
main_req_2_04_test();

main_req_2_06_test();
main_req_2_07_test();
Expand All @@ -75,7 +76,6 @@ main(void)
main_req_3_19_test();
main_req_3_20_test();
main_req_3_21_test();

main_req_3_22_test();
main_req_3_23_test();

Expand Down
8 changes: 7 additions & 1 deletion pstop_c/pstop/test/src/pstop/requirements/req_2_03_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ req_2_03_1_test(void)
pstop_msg_t resp;
pstop_message_init(&resp);

set_time(100);
// succesfull bond
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
TEST_ASSERT_EQUAL(PSTOP_MESSAGE_BOND, resp.message);
Expand All @@ -72,6 +73,7 @@ req_2_03_1_test(void)
req.stamp = 110;
req.received_counter = resp.counter;
req.received_stamp = resp.stamp;
set_time(110);
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
Expand All @@ -81,9 +83,10 @@ req_2_03_1_test(void)
// now send message with a counter too far in the future
req.message = PSTOP_MESSAGE_STOP;
req.counter = 14;
req.stamp = 110;
req.stamp = 120;
req.received_counter = resp.counter;
req.received_stamp = resp.stamp;
set_time(120);
TEST_ASSERT_EQUAL(PSTOP_MSG_LOST, machine_process_message(&machine, &req, &resp));
remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
Expand Down Expand Up @@ -125,6 +128,7 @@ req_2_03_2_test(void)
pstop_message_init(&resp);

// succesfull bond
set_time(100);
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
TEST_ASSERT_EQUAL(PSTOP_MESSAGE_BOND, resp.message);

Expand All @@ -138,6 +142,7 @@ req_2_03_2_test(void)
req.stamp = 110;
req.received_counter = resp.counter;
req.received_stamp = resp.stamp;
set_time(110);
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
Expand All @@ -150,6 +155,7 @@ req_2_03_2_test(void)
req.stamp = 1000;
req.received_counter = resp.counter;
req.received_stamp = resp.stamp;
set_time(1000);
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
Expand Down
102 changes: 102 additions & 0 deletions pstop_c/pstop/test/src/pstop/requirements/req_2_04_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: 2026 Polymath Robotics, Inc.
// SPDX-License-Identifier: Apache-2.0

#include "pstop/protocol.h"

#include <unity/unity.h>

#include "pstop/checksum.h"
#include "pstop/requirements/test_utils.h"

#define MACHINE_ID 1236
#define PSTOP_ID 1234

static
device_id_t MACHINE = {
.data = MACHINE_ID
};

static
device_id_t REMOTE = {
.data = PSTOP_ID
};

static
pstop_application_t pstop_app;

#define MAX_CLIENTS 2U

static pstop_remote_data_t pstop_clients[MAX_CLIENTS];

// 2-04-1: Shall be able to detect delayed messages
// Description: Set number of missed heartbeats to 2. Send BOND message
// with timestamp 100. Send OK message with timestamp 200, current
// clock is 200. Then send another OK message with timestamp 900 and
// current clock is 300. This will indicate we missed too many
// messages and will return PSTOP_MSG_LOST.

static

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — req_2_04_1_test()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — req_2_04_1_test()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — req_2_04_1_test()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — req_2_04_1_test()

fans out to 7 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

void
req_2_04_1_test(void)
{
pstop_machine_t machine;
machine_init(&machine, &pstop_app, pstop_clients, MAX_CLIENTS);
configure_app_defaults(&pstop_app, &MACHINE, 0U, 2U, 1000U);

set_operator_allowed(true, false, 500U);

pstop_msg_t req;
pstop_message_init(&req);
req.message = PSTOP_MESSAGE_BOND;
req.counter = 10;
req.stamp = 100;
req.id.data = PSTOP_ID;
req.receiver_id.data = MACHINE_ID;
req.received_counter = 0U;
req.received_stamp = 0U;

pstop_msg_t resp;
pstop_message_init(&resp);

set_time(100);
// succesfull bond
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
TEST_ASSERT_EQUAL(PSTOP_MESSAGE_BOND, resp.message);

const protocol_data_t *remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
uint64_t last_heartbeat = remote->last_timestamp;

// now send message with a valid stop
req.message = PSTOP_MESSAGE_OK;
req.counter = 11;
req.stamp = 200;
req.received_counter = resp.counter;
req.received_stamp = resp.stamp;
set_time(200);
TEST_ASSERT_EQUAL(PSTOP_OK, machine_process_message(&machine, &req, &resp));
remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
TEST_ASSERT_NOT_EQUAL(last_heartbeat, remote->last_timestamp);
last_heartbeat = remote->last_timestamp;

// now send message with timestamp too far in the future
req.message = PSTOP_MESSAGE_OK;
req.counter = 12;
req.stamp = 900;
req.received_counter = resp.counter;
req.received_stamp = resp.stamp;
set_time(300);
TEST_ASSERT_EQUAL(PSTOP_MSG_LOST, machine_process_message(&machine, &req, &resp));
remote = machine_get_protocol_data(&machine, &REMOTE);
TEST_ASSERT_NOT_NULL(remote);
TEST_ASSERT_EQUAL(last_heartbeat, remote->last_timestamp);
}

void
main_req_2_04_test(void)
{
UnitySetTestFile("req_2_04_test.c");

RUN_TEST(req_2_04_1_test);
}
Loading