/* * This program source code file is part of SystemK, a library in the KTag project. * * 🛡️ 🃞 * * Copyright © 2016-2025 Joseph P. Kearney and the KTag developers. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * There should be a copy of the GNU Affero General Public License in the LICENSE * file in the root of this repository. If not, see . */ /* Include Files */ #include "SystemK.h" //! Activities for the top-level state machine. /*! * This array is indexed by #StateID_T (except there is no entry for * #STATE_OUT_OF_RANGE or #STATE_NONE), and the order of activities * must match the order of the enumeration. */ static const StateActivity_T * Activities[] = { &STATE_INITIALIZING_Activities, &STATE_REPROGRAMMING_Activities, &STATE_CONFIGURING_Activities, &STATE_READY_Activities, &STATE_STARTING_GAME__INSTIGATING_Activities, &STATE_STARTING_GAME__RESPONDING_Activities, &STATE_STARTING_GAME__COUNTING_DOWN_Activities, &STATE_PLAYING__INTERACTING_Activities, &STATE_PLAYING__TAGGED_OUT_Activities, &STATE_WRAPPING_UP_Activities }; //! Context in which the top-level state machine is run. static StateMachineContext_T Context = { .States = { .Previous_State = STATE_NONE, .Current_State = STATE_NONE, .Next_State = STATE_INITIALIZING }, .Time_At_State_Entry_In_Ticks = 0, }; StateMachineContext_T* GetContext() { return &Context; } TaskHandle_t State_Machine_Task_Handle; void State_Machine_Task(void * pvParameters) { while (true) { // Handle state changes. if (Context.States.Next_State != Context.States.Current_State) { // Exit the current state. if (Context.States.Current_State < STATE_IS_OUT_OF_RANGE) { if (Activities[Context.States.Current_State]->Exit != NULL) { Activities[Context.States.Current_State]->Exit(&Context); } } // Save off the time of the state change. Context.Time_At_State_Entry_In_Ticks = xTaskGetTickCount(); // Update the current state. Context.States.Previous_State = Context.States.Current_State; Context.States.Current_State = Context.States.Next_State; if (Context.States.Current_State < STATE_IS_OUT_OF_RANGE) { // Enter the current state. if (Activities[Context.States.Current_State]->Entry != NULL) { Activities[Context.States.Current_State]->Entry(&Context); } } } if (Context.States.Current_State < STATE_IS_OUT_OF_RANGE) { // Execute the current state. if (Activities[Context.States.Current_State]->Do != NULL) { Activities[Context.States.Current_State]->Do(&Context); } } vTaskDelay(1); } } void ProcessUnhandledEvent(KEvent_T * Event) { switch (Event->ID) { case KEVENT_TAG_RECEIVED: FreeDecodedPacketBuffer(Event->Data); break; case KEVENT_COMMAND_RECEIVED: FreeDecodedPacketBuffer(Event->Data); break; case KEVENT_BLE_PACKET_RECEIVED: BLE_FreePacketBuffer(Event->Data); break; default: // All other events can be safely ignored. break; } } void HandleBLEEventPacket(const BLE_EventPacket_T *const packet, StateMachineContext_T *context) { if (BLE_IsBLEPacketForMe(packet->target_BD_ADDR)) { if (BLE_IsPacketNew(packet->BD_ADDR, BLE_PACKET_TYPE_EVENT, packet->event_number)) { if (packet->event_ID == BLE_EVENT_CONFIGURED) { if (context->States.Current_State == STATE_CONFIGURING) { static KEvent_T Event = { .ID = KEVENT_BLE_EVENT_RECEIVED, .Data = (void *)BLE_EVENT_CONFIGURED }; Transition_For_Event(context, STATE_READY, &Event); } } else if (packet->event_ID == BLE_EVENT_CONFIGURE) { if (context->States.Current_State == STATE_READY) { static KEvent_T Event = { .ID = KEVENT_BLE_EVENT_RECEIVED, .Data = (void *)BLE_EVENT_CONFIGURE }; Transition_For_Event(context, STATE_CONFIGURING, &Event); } } else if (packet->event_ID == BLE_EVENT_WRAPUP_COMPLETE) { if (context->States.Current_State == STATE_WRAPPING_UP) { StateID_T next_state = (StateID_T)packet->event_data; if (next_state < STATE_IS_OUT_OF_RANGE) { static KEvent_T Event = { .ID = KEVENT_BLE_EVENT_RECEIVED, .Data = (void *)BLE_EVENT_WRAPUP_COMPLETE }; Transition_For_Event(context, next_state, &Event); } else { KLOG_WARN("STATE", "Received a BLE Wrapup Complete event with an invalid Next State!"); } } } else if (packet->event_ID == BLE_EVENT_GAME_OVER) { if ((context->States.Current_State == STATE_PLAYING__INTERACTING) || (context->States.Current_State == STATE_PLAYING__TAGGED_OUT)) { KEvent_T game_over_event = { .ID = KEVENT_GAME_OVER, .Data = (void *)0x00 }; Post_KEvent(&game_over_event); } } } } BLE_FreePacketBuffer((BLE_GenericPacketType_T *)packet); }