/** * @file tests/can_net_node_error.cpp * @brief CAN Net node events (for application) * @date Dec 14, 2016 * @author jjacobs * @copyright 2016 Dual Inventive Technology Centre B.V. */ #include "fixtures/CANTest.hpp" extern "C" { #include "time.c" } /** * Test DI_CAN_NET_CANID_MSG_NODE_ERROR macro */ TEST_F(DI_CAN, CANID_MSG_NODE_ERROR) { struct di_can_msg *msg; msg = di_can_msg_alloc_with_canid(&_ctx, DI_CAN_NET_CANID_MSG_NODE_ERROR(PUBLISH)); ASSERT_NE(nullptr, msg); ASSERT_EQ(DI_CAN_MSGTYPE_NET, DI_CAN_GET_MSGTYPE(msg->canid)); ASSERT_EQ(DI_CAN_TRANSFERTYPE_PUBLISH, DI_CAN_GET_TRANSFERTYPE(msg->canid)); ASSERT_EQ(DI_CAN_NET_DTYPE_NODE_ERROR, DI_CAN_GET_DATATYPE(msg->canid)); di_can_msg_free(&msg); } /** * Test di_can_net_serialize_node_error */ TEST_F(DI_CAN, net_serialize_node_error) { struct di_can_msg *msg; msg = di_can_msg_alloc_with_canid(&_ctx, DI_CAN_NET_CANID_MSG_NODE_ERROR(PUBLISH)); ASSERT_NE(nullptr, msg); di_can_net_serialize_node_error(msg, DNE_FIRMWARE_TILT); ASSERT_EQ(4U, msg->size); di_can_msg_free(&msg); } /** * Test di_can_net_publish_node_error */ TEST_F(DI_CAN, net_publish_node_error) { ASSERT_EQ(DNOK, di_can_net_publish_node_error(&_ctx, DNE_FIRMWARE_TILT)); struct di_can_msg *msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DI_CAN_TRANSFERTYPE_PUBLISH, DI_CAN_GET_TRANSFERTYPE(msg->canid)); ASSERT_EQ(DI_CAN_NET_DTYPE_NODE_ERROR, DI_CAN_GET_DATATYPE(msg->canid)); ASSERT_EQ(DI_CAN_NODEID_BROADCAST, msg->dst_id); ASSERT_EQ(DI_CAN_PTYPE_DI_ERRNO, msg->ptype); ASSERT_EQ(4U, msg->size); di_errno_t v; ASSERT_EQ(DNOK, di_can_msg_read_data_di_errno(msg, &v)); ASSERT_EQ(DNE_FIRMWARE_TILT, v); di_can_msg_free(&msg); } /** * Test node error: * * DI_CAN_NET_CALLBACK_NODE_ERROR_PUBLISH_ITEM * * di_can_net_cb_node_error_publish */ TEST_F(DI_CAN, net_cb_node_error_publish) { static struct di_can_callback callbacks[] = { DI_CAN_NET_CALLBACK_NODE_ERROR_PUBLISH_ITEM }; struct di_can_msg *msg; ASSERT_EQ(DNOK, di_can_callback_init(&_ctx, callbacks, DI_ARRAY_SIZE(callbacks))); ASSERT_EQ(DNOK, di_can_net_publish_node_error(&_ctx, DNE_FIRMWARE_TILT)); msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DNOK, di_can_callback_execute(&_ctx, msg)); di_can_msg_free(&msg); } /** * Test net node error event callback * * Publish node error with di_can_net_publish_node_error * * Recv the published message and feed it to di_can_callback_execute * * Check in the callback * * ev == DI_CAN_NET_NODE_EVENT_ERROR * * node->nodeid == 0xdeadbeef * * node->error == DNE_FIRMWARE_TILT * * node->type == DI_CAN_NET_NODE_TYPE_UNKNOWN (undiscovered node) * * node->role == DI_CAN_NET_NODE_ROLE_UNKNOWN (undiscovered node) * * Publish again node error with di_can_net_publish_node_error * * Recv the published message and feed it to di_can_callback_execute * * Check if callback is not run * * Publish node error with di_can_net_publish_node_error * * Recv the published message and feed it to di_can_callback_execute * * Check in the callback * * ev == DI_CAN_NET_NODE_EVENT_ERROR * * node->nodeid == 0xdeadbeef * * node->error == DNOK * * node->type == DI_CAN_NET_NODE_TYPE_UNKNOWN (undiscovered node) * * node->role == DI_CAN_NET_NODE_ROLE_UNKNOWN (undiscovered node) */ static size_t net_node_error_event_cb_ctr = 0; /**< Event callback fired counter */ TEST_F(DI_CAN, net_node_error_event) { void net_node_error_event_cb(const struct di_can_net *ctx, const enum di_can_net_node_events ev, const struct di_can_net_node *node); static struct di_can_callback callbacks[] = { DI_CAN_NET_CALLBACK_NODE_ERROR_PUBLISH_ITEM }; struct di_can_msg *msg; ASSERT_EQ(DNOK, di_can_callback_init(&_ctx, callbacks, DI_ARRAY_SIZE(callbacks))); di_can_net_node_set_event_callback(&_ctx, net_node_error_event_cb); /* Publish DNE_FIRMWARE_TILT as 0xcafebabe node */ SetNodeid(0xcafebabe); di_can_net_publish_node_error(&_ctx, DNE_FIRMWARE_TILT); /* Receive publish msg and execute net_node_error_event_cb on it */ msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DNOK, di_can_callback_execute(&_ctx, msg)); di_can_msg_free(&msg); /* Callback should have run exactly once */ ASSERT_EQ(1U, net_node_error_event_cb_ctr); /* Publish second DNE_FIRMWARE_TILT as 0xcafebabe node */ di_can_net_publish_node_error(&_ctx, DNE_FIRMWARE_TILT); /* Receive publish msg and execute net_node_error_event_cb on it */ msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DNOK, di_can_callback_execute(&_ctx, msg)); di_can_msg_free(&msg); /* Callback should have run exactly once, second same node_error is ignored */ ASSERT_EQ(1U, net_node_error_event_cb_ctr); /* Publish second DNOK as 0xcafebabe node */ di_can_net_publish_node_error(&_ctx, DNOK); /* Receive publish msg and execute net_node_error_event_cb on it */ msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DNOK, di_can_callback_execute(&_ctx, msg)); di_can_msg_free(&msg); /* Callback should have run exactly once, second same node_error is ignored */ ASSERT_EQ(2U, net_node_error_event_cb_ctr); } void net_node_error_event_cb(const struct di_can_net *ctx, const enum di_can_net_node_events ev, const struct di_can_net_node *node) { (void)ctx; printf("[%s] event(%d): node: %04x, type: %02x, role: %02x\n", __func__, ev, node->nodeid, node->type, node->role); ASSERT_EQ(DI_CAN_NET_NODE_EVENT_ERROR, ev); ASSERT_EQ(0xcafebabe, node->nodeid); /* Check on first callback if the error is DNE_FIRMWARE_TILT, second callback DNOK */ if (net_node_error_event_cb_ctr == 0) ASSERT_EQ(DNE_FIRMWARE_TILT, node->error); else if (net_node_error_event_cb_ctr == 1) ASSERT_EQ(DNOK, node->error); else ASSERT_TRUE(false); /* The node type and role are unknown because the node was not previously discovered */ ASSERT_EQ(DI_CAN_NET_NODE_TYPE_UNKNOWN, node->type); ASSERT_EQ(DI_CAN_NET_NODE_ROLE_UNKNOWN, node->role); net_node_error_event_cb_ctr++; } /** * Verify di_can_net_execute publishes self node error when !DNOK */ TEST_F(DI_CAN, net_self_execute_error_publish) { struct di_can_msg *msg; // Set error, check if it is directly published. Then truncate the send message di_can_net_self_set_error(&_ctx, DNE_FIRMWARE_TILT); msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); di_can_msg_free(&msg); // Set time after the self node error timeout exact on DI_CAN_NET_CFG_SELF_ERROR_PUBLISH_MS, then run di_can_net_execute _ctx.net.discover.state = DI_CAN_NET_DISCOVER_STATE_FINISH; _di_time_uptime = DI_CAN_NET_CFG_SELF_ERROR_PUBLISH_MS; di_can_net_execute(&_ctx); // Receive the published self node error message send by di_can_net_execute msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DI_CAN_NET_DTYPE_NODE_ERROR, DI_CAN_GET_DATATYPE(msg->canid)); di_can_msg_free(&msg); // Send next publish with net execute _di_time_uptime = (DI_CAN_NET_CFG_SELF_ERROR_PUBLISH_MS * 2) + 1; di_can_net_execute(&_ctx); // Receive the published self node error message send by di_can_net_execute msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); ASSERT_EQ(DI_CAN_NET_DTYPE_NODE_ERROR, DI_CAN_GET_DATATYPE(msg->canid)); di_can_msg_free(&msg); // Clear self node error by setting it to DNOK di_can_net_self_set_error(&_ctx, DNOK); msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_NE(nullptr, msg); // Check DNOK is published by di_can_net_self_set_error di_errno_t err; ASSERT_EQ(DNOK, di_can_msg_read_data_di_errno(msg, &err)); ASSERT_EQ(DNOK, err); di_can_msg_free(&msg); // Check di_can_net_execute doesn't publish when self node error == DNOK _di_time_uptime = (DI_CAN_NET_CFG_SELF_ERROR_PUBLISH_MS * 3); di_can_net_execute(&_ctx); // Receive the published self node error message send by di_can_net_execute msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_ANY); ASSERT_EQ(nullptr, msg); }