Skip to content

Commit a92e6f0

Browse files
committed
cloud: fix fallback provisioning network state handling
The fallback provisioning logic incorrectly determined the next state when provisioning failed. The network_connected variable was not being updated properly, causing incorrect state transitions. This fix ensures the network connection status is checked accurately before determining whether to retry provisioning or transition to an offline state. Added corresponding unit tests to verify the correct behavior of the fallback provisioning logic under different network conditions. Signed-off-by: Simen S. Røstad <simen.rostad@nordicsemi.no>
1 parent 6c63bd8 commit a92e6f0

3 files changed

Lines changed: 136 additions & 36 deletions

File tree

app/src/modules/cloud/cloud.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ struct cloud_state_object {
160160
/* Last received message */
161161
uint8_t msg_buf[MAX_MSG_SIZE];
162162

163-
/* Network status */
164-
enum network_msg_type nw_status;
163+
/* Last network connection status */
164+
bool network_connected;
165165

166166
/* Provisioning ongoing flag */
167167
bool provisioning_ongoing;
@@ -877,9 +877,13 @@ static void state_connecting_provisioning_run(void *obj)
877877
smf_set_state(SMF_CTX(state_object), &states[STATE_PROVISIONED]);
878878

879879
return;
880-
} else if (msg == CLOUD_PROVISIONING_FAILED) {
880+
} else if (msg == CLOUD_PROVISIONING_FAILED && state_object->network_connected) {
881881
smf_set_state(SMF_CTX(state_object), &states[STATE_CONNECTING_BACKOFF]);
882882

883+
return;
884+
} else if (msg == CLOUD_PROVISIONING_FAILED && !state_object->network_connected) {
885+
smf_set_state(SMF_CTX(state_object), &states[STATE_DISCONNECTED]);
886+
883887
return;
884888
}
885889
}
@@ -1483,6 +1487,19 @@ static void state_connected_paused_run(void *obj)
14831487
}
14841488
}
14851489

1490+
static void network_connection_status_retain(struct cloud_state_object *state_object)
1491+
{
1492+
if (state_object->chan == &NETWORK_CHAN) {
1493+
struct network_msg msg = MSG_TO_NETWORK_MSG(state_object->msg_buf);
1494+
1495+
if (msg.type == NETWORK_DISCONNECTED || msg.type == NETWORK_CONNECTED) {
1496+
/* Update network status to retain the last connection status */
1497+
state_object->network_connected =
1498+
(msg.type == NETWORK_CONNECTED) ? true : false;
1499+
}
1500+
}
1501+
}
1502+
14861503
static void cloud_module_thread(void)
14871504
{
14881505
int err;
@@ -1525,6 +1542,8 @@ static void cloud_module_thread(void)
15251542
return;
15261543
}
15271544

1545+
network_connection_status_retain(&cloud_state);
1546+
15281547
err = smf_run_state(SMF_CTX(&cloud_state));
15291548
if (err) {
15301549
LOG_ERR("smf_run_state(), error: %d", err);

tests/module/cloud/src/cloud_module_test.c

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ static void connect_cloud(void)
211211
err = zbus_chan_pub(&NETWORK_CHAN, &nw, K_NO_WAIT);
212212
TEST_ASSERT_EQUAL(0, err);
213213

214-
k_sleep(K_MSEC(100));
214+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
215215
}
216216

217217
static void dummy_cb(const struct zbus_channel *chan)
@@ -516,7 +516,7 @@ void test_connected_disconnected_to_connected_send_payload_disconnect(void)
516516
TEST_ASSERT_EQUAL(0, err);
517517

518518
/* Transport module needs CPU to run state machine */
519-
k_sleep(K_MSEC(100));
519+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
520520

521521
err = k_sem_take(&cloud_connected, K_SECONDS(1));
522522
TEST_ASSERT_EQUAL(0, err);
@@ -525,7 +525,7 @@ void test_connected_disconnected_to_connected_send_payload_disconnect(void)
525525
TEST_ASSERT_EQUAL(0, err);
526526

527527
/* Transport module needs CPU to run state machine */
528-
k_sleep(K_MSEC(100));
528+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
529529

530530
TEST_ASSERT_EQUAL(1, nrf_cloud_coap_json_message_send_fake.call_count);
531531
TEST_ASSERT_EQUAL(0, strncmp(nrf_cloud_coap_json_message_send_fake.arg0_val,
@@ -539,7 +539,7 @@ void test_connected_disconnected_to_connected_send_payload_disconnect(void)
539539
TEST_ASSERT_EQUAL(0, err);
540540

541541
/* Transport module needs CPU to run state machine */
542-
k_sleep(K_MSEC(100));
542+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
543543

544544
err = k_sem_take(&cloud_disconnected, K_SECONDS(1));
545545
TEST_ASSERT_EQUAL(0, err);
@@ -597,7 +597,7 @@ void test_gnss_location_data_handling(void)
597597
TEST_ASSERT_EQUAL(0, err);
598598

599599
/* Give the module time to process */
600-
k_sleep(K_MSEC(100));
600+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
601601

602602
/* Verify that GNSS location data was sent to nRF Cloud */
603603
TEST_ASSERT_EQUAL(1, nrf_cloud_coap_location_send_fake.call_count);
@@ -629,7 +629,7 @@ void test_storage_data_battery_sent_to_cloud(void)
629629
TEST_ASSERT_EQUAL(0, err);
630630

631631
/* Allow processing */
632-
k_sleep(K_MSEC(100));
632+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
633633

634634
/* One successful read + one -EAGAIN drain */
635635
TEST_ASSERT_EQUAL(2, storage_batch_read_fake.call_count);
@@ -657,7 +657,7 @@ void test_storage_data_environmental_sent_to_cloud(void)
657657
ARG_UNUSED(err);
658658

659659
/* Allow processing */
660-
k_sleep(K_MSEC(100));
660+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
661661

662662
/* One successful read + one -EAGAIN drain */
663663
TEST_ASSERT_EQUAL(2, storage_batch_read_fake.call_count);
@@ -685,14 +685,103 @@ void test_storage_data_network_conn_eval_sent_to_cloud(void)
685685
ARG_UNUSED(err);
686686

687687
/* Allow processing */
688-
k_sleep(K_MSEC(100));
688+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
689689

690690
/* One successful read + one -EAGAIN drain */
691691
TEST_ASSERT_EQUAL(2, storage_batch_read_fake.call_count);
692692
/* Expect two sensor publishes: CONEVAL and RSRP */
693693
TEST_ASSERT_EQUAL(2, nrf_cloud_coap_sensor_send_fake.call_count);
694694
}
695695

696+
void test_provisioning_failed_with_network_connected_should_go_to_backoff(void)
697+
{
698+
int err;
699+
struct network_msg network_msg = {
700+
.type = NETWORK_CONNECTED
701+
};
702+
struct cloud_msg cloud_msg = {
703+
.type = CLOUD_PROVISIONING_REQUEST
704+
};
705+
struct nrf_provisioning_callback_data event = {
706+
.type = NRF_PROVISIONING_EVENT_FAILED
707+
};
708+
709+
/* Start with a connected network state */
710+
zbus_chan_pub(&NETWORK_CHAN, &network_msg, K_NO_WAIT);
711+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
712+
713+
/* Should now be connected */
714+
err = k_sem_take(&cloud_connected, K_SECONDS(WAIT_TIMEOUT));
715+
TEST_ASSERT_EQUAL(0, err);
716+
717+
/* Trigger provisioning request */
718+
zbus_chan_pub(&CLOUD_CHAN, &cloud_msg, K_NO_WAIT);
719+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
720+
721+
/* Should now be disconnected (entering provisioning state) */
722+
err = k_sem_take(&cloud_disconnected, K_SECONDS(WAIT_TIMEOUT));
723+
TEST_ASSERT_EQUAL(0, err);
724+
725+
TEST_ASSERT_EQUAL(1, nrf_provisioning_trigger_manually_fake.call_count);
726+
727+
/* Simulate provisioning failure while network is still connected */
728+
handler(&event);
729+
730+
/* Allow time for state machine to process the failure and enter backoff state */
731+
k_sleep(K_SECONDS(INITIAL_PROVISIONING_RETRY_SEC + 1));
732+
733+
/* Verify that provisioning is retried after backoff
734+
* (indicating we went to backoff state)
735+
*/
736+
TEST_ASSERT_EQUAL(2, nrf_provisioning_trigger_manually_fake.call_count);
737+
}
738+
739+
void test_provisioning_failed_with_network_disconnected_should_go_to_disconnected(void)
740+
{
741+
int err;
742+
struct network_msg network_msg = {
743+
.type = NETWORK_CONNECTED
744+
};
745+
struct cloud_msg cloud_msg = {
746+
.type = CLOUD_PROVISIONING_REQUEST
747+
};
748+
struct nrf_provisioning_callback_data event = {
749+
.type = NRF_PROVISIONING_EVENT_FAILED
750+
};
751+
752+
/* Start with a connected network state */
753+
zbus_chan_pub(&NETWORK_CHAN, &network_msg, K_NO_WAIT);
754+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
755+
756+
/* Should now be connected */
757+
err = k_sem_take(&cloud_connected, K_SECONDS(WAIT_TIMEOUT));
758+
TEST_ASSERT_EQUAL(0, err);
759+
760+
/* Trigger provisioning request */
761+
zbus_chan_pub(&CLOUD_CHAN, &cloud_msg, K_NO_WAIT);
762+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
763+
764+
/* Should now be disconnected (entering provisioning state) */
765+
err = k_sem_take(&cloud_disconnected, K_SECONDS(WAIT_TIMEOUT));
766+
TEST_ASSERT_EQUAL(0, err);
767+
768+
TEST_ASSERT_EQUAL(1, nrf_provisioning_trigger_manually_fake.call_count);
769+
770+
/* Simulate network disconnect while in provisioning state */
771+
network_msg.type = NETWORK_DISCONNECTED;
772+
zbus_chan_pub(&NETWORK_CHAN, &network_msg, K_NO_WAIT);
773+
k_sleep(K_MSEC(PROCESSING_DELAY_MS));
774+
775+
/* Simulate provisioning failure while network is disconnected */
776+
handler(&event);
777+
778+
/* Allow time for state machine to process the failure and enter disconnected state */
779+
k_sleep(K_SECONDS(INITIAL_PROVISIONING_RETRY_SEC + 1));
780+
781+
/* Verify that provisioning is NOT retried (indicating we went to disconnected state) */
782+
TEST_ASSERT_EQUAL(1, nrf_provisioning_trigger_manually_fake.call_count);
783+
}
784+
696785
/* This is required to be added to each test. That is because unity's
697786
* main may return nonzero, while zephyr's main currently must
698787
* return 0 in all cases (other values are reserved).
Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,41 @@
11
@startuml
2-
[*] --> STATE_RUNNING
3-
42
state STATE_RUNNING {
53
[*] --> STATE_DISCONNECTED
64

75
state STATE_DISCONNECTED
86
state STATE_CONNECTING
97
state STATE_CONNECTED
108

11-
STATE_DISCONNECTED --> STATE_CONNECTING: NETWORK_CONNECTED
12-
STATE_CONNECTING --> STATE_DISCONNECTED: NETWORK_DISCONNECTED
9+
STATE_DISCONNECTED --> STATE_CONNECTING : NETWORK_CONNECTED
1310

1411
state STATE_CONNECTING {
1512
[*] --> STATE_CONNECTING_ATTEMPT
1613

1714
state STATE_CONNECTING_ATTEMPT {
1815
[*] --> STATE_PROVISIONED
19-
state STATE_PROVISIONED
20-
state STATE_PROVISIONING
2116

22-
STATE_PROVISIONED --> STATE_PROVISIONING: CLOUD_NOT_AUTHENTICATED
23-
STATE_PROVISIONED --> STATE_CONNECTED: CLOUD_CONNECTION_SUCCESS
24-
STATE_PROVISIONED --> STATE_CONNECTING_BACKOFF: CLOUD_CONNECTION_FAILED
25-
26-
STATE_PROVISIONING --> STATE_PROVISIONED: CLOUD_PROVISIONING_FINISHED
27-
STATE_PROVISIONING --> STATE_CONNECTING_BACKOFF: CLOUD_PROVISIONING_FAILED
17+
STATE_PROVISIONED --> STATE_PROVISIONING : CLOUD_NOT_AUTHENTICATED
18+
STATE_PROVISIONED --> STATE_CONNECTED : CLOUD_CONNECTION_SUCCESS
19+
STATE_PROVISIONED --> STATE_CONNECTING_BACKOFF : CLOUD_CONNECTION_FAILED
2820
}
2921

30-
state STATE_CONNECTING_BACKOFF
31-
32-
STATE_CONNECTING_BACKOFF --> STATE_PROVISIONED: CLOUD_BACKOFF_EXPIRED && !provisioning_ongoing
33-
STATE_CONNECTING_BACKOFF --> STATE_PROVISIONING: CLOUD_BACKOFF_EXPIRED && provisioning_ongoing
22+
STATE_CONNECTING --> STATE_DISCONNECTED : NETWORK_DISCONNECTED
23+
STATE_PROVISIONING --> STATE_PROVISIONED : CLOUD_PROVISIONING_FINISHED
24+
STATE_PROVISIONING --> STATE_CONNECTING_BACKOFF : CLOUD_PROVISIONING_FAILED && network_connected
25+
STATE_PROVISIONING --> STATE_DISCONNECTED : CLOUD_PROVISIONING_FAILED && !network_connected
26+
STATE_CONNECTING_BACKOFF --> STATE_PROVISIONED : CLOUD_BACKOFF_EXPIRED && !provisioning_ongoing
27+
STATE_CONNECTING_BACKOFF --> STATE_PROVISIONING : CLOUD_BACKOFF_EXPIRED && provisioning_ongoing
3428
}
3529

3630
state STATE_CONNECTED {
3731
[*] --> STATE_CONNECTED_READY
3832

39-
state STATE_CONNECTED_READY
40-
state STATE_CONNECTED_PAUSED
41-
42-
STATE_CONNECTED_READY --> STATE_CONNECTED_PAUSED: NETWORK_DISCONNECTED
43-
STATE_CONNECTED_PAUSED --> STATE_CONNECTED_READY: NETWORK_CONNECTED
44-
45-
STATE_CONNECTED_READY --> STATE_CONNECTING: CLOUD_SEND_REQUEST_FAILED
46-
STATE_CONNECTED_READY --> STATE_PROVISIONING: CLOUD_PROVISIONING_REQUEST
33+
STATE_CONNECTED_READY --> STATE_CONNECTED_PAUSED : NETWORK_DISCONNECTED
34+
STATE_CONNECTED_READY --> STATE_CONNECTING : CLOUD_SEND_REQUEST_FAILED
35+
STATE_CONNECTED_READY --> STATE_PROVISIONING : CLOUD_PROVISIONING_REQUEST
36+
STATE_CONNECTED_PAUSED --> STATE_CONNECTED_READY : NETWORK_CONNECTED
4737
}
4838
}
49-
@enduml
39+
40+
[*] --> STATE_RUNNING
41+
@enduml

0 commit comments

Comments
 (0)