| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "TicketService.h" | ||
| 2 | #include "esp_log.h" | ||
| 3 | #include "esp_timer.h" | ||
| 4 | |||
| 5 | static const char* TAG = "TicketService"; | ||
| 6 | |||
| 7 | ✗ | TicketService::TicketService(uint32_t capacity) | |
| 8 | ✗ | : m_capacity(capacity) | |
| 9 | ✗ | , m_nextTicketId(1) { | |
| 10 | ✗ | m_mutex = xSemaphoreCreateMutex(); | |
| 11 | ✗ | if (!m_mutex) { | |
| 12 | ✗ | ESP_LOGE(TAG, "Failed to create mutex"); | |
| 13 | } | ||
| 14 | |||
| 15 | ✗ | ESP_LOGI(TAG, "TicketService created (capacity: %lu)", capacity); | |
| 16 | ✗ | } | |
| 17 | |||
| 18 | ✗ | TicketService::~TicketService() { | |
| 19 | ✗ | if (m_mutex) { | |
| 20 | ✗ | vSemaphoreDelete(m_mutex); | |
| 21 | } | ||
| 22 | ✗ | } | |
| 23 | |||
| 24 | ✗ | uint32_t TicketService::getNewTicket() { | |
| 25 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 26 | // Check capacity | ||
| 27 | ✗ | uint32_t activeCount = 0; | |
| 28 | ✗ | for (const auto& [id, ticket] : m_tickets) { | |
| 29 | ✗ | if (!ticket.isUsed) { | |
| 30 | ✗ | activeCount++; | |
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | ✗ | if (activeCount >= m_capacity) { | |
| 35 | ✗ | ESP_LOGW(TAG, "Parking full! Cannot issue new ticket (capacity: %lu)", m_capacity); | |
| 36 | ✗ | xSemaphoreGive(m_mutex); | |
| 37 | ✗ | return 0; // Capacity reached | |
| 38 | } | ||
| 39 | |||
| 40 | // Create new ticket | ||
| 41 | ✗ | uint32_t ticketId = m_nextTicketId++; | |
| 42 | ✗ | Ticket ticket(ticketId, esp_timer_get_time()); | |
| 43 | ✗ | m_tickets[ticketId] = ticket; | |
| 44 | |||
| 45 | ✗ | ESP_LOGI(TAG, "New ticket issued: ID=%lu (active: %lu/%lu)", ticketId, activeCount + 1, m_capacity); | |
| 46 | |||
| 47 | ✗ | xSemaphoreGive(m_mutex); | |
| 48 | ✗ | return ticketId; | |
| 49 | } | ||
| 50 | |||
| 51 | ✗ | return 0; | |
| 52 | } | ||
| 53 | |||
| 54 | ✗ | bool TicketService::payTicket(uint32_t ticketId) { | |
| 55 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 56 | ✗ | auto it = m_tickets.find(ticketId); | |
| 57 | ✗ | if (it == m_tickets.end()) { | |
| 58 | ✗ | ESP_LOGW(TAG, "Ticket not found: ID=%lu", ticketId); | |
| 59 | ✗ | xSemaphoreGive(m_mutex); | |
| 60 | ✗ | return false; | |
| 61 | } | ||
| 62 | |||
| 63 | ✗ | if (it->second.isPaid) { | |
| 64 | ✗ | ESP_LOGW(TAG, "Ticket already paid: ID=%lu", ticketId); | |
| 65 | ✗ | xSemaphoreGive(m_mutex); | |
| 66 | ✗ | return true; // Already paid is not an error | |
| 67 | } | ||
| 68 | |||
| 69 | ✗ | it->second.isPaid = true; | |
| 70 | ✗ | it->second.paymentTimestamp = esp_timer_get_time(); | |
| 71 | |||
| 72 | ✗ | ESP_LOGI(TAG, "Ticket paid: ID=%lu", ticketId); | |
| 73 | |||
| 74 | ✗ | xSemaphoreGive(m_mutex); | |
| 75 | ✗ | return true; | |
| 76 | } | ||
| 77 | |||
| 78 | ✗ | return false; | |
| 79 | } | ||
| 80 | |||
| 81 | ✗ | bool TicketService::validateAndUseTicket(uint32_t ticketId) { | |
| 82 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 83 | ✗ | auto it = m_tickets.find(ticketId); | |
| 84 | ✗ | if (it == m_tickets.end()) { | |
| 85 | ✗ | ESP_LOGW(TAG, "Ticket not found: ID=%lu", ticketId); | |
| 86 | ✗ | xSemaphoreGive(m_mutex); | |
| 87 | ✗ | return false; | |
| 88 | } | ||
| 89 | |||
| 90 | ✗ | if (it->second.isUsed) { | |
| 91 | ✗ | ESP_LOGW(TAG, "Ticket already used: ID=%lu", ticketId); | |
| 92 | ✗ | xSemaphoreGive(m_mutex); | |
| 93 | ✗ | return false; | |
| 94 | } | ||
| 95 | |||
| 96 | ✗ | if (!it->second.isPaid) { | |
| 97 | ✗ | ESP_LOGW(TAG, "Ticket not paid: ID=%lu", ticketId); | |
| 98 | ✗ | xSemaphoreGive(m_mutex); | |
| 99 | ✗ | return false; | |
| 100 | } | ||
| 101 | |||
| 102 | // Mark as used | ||
| 103 | ✗ | it->second.isUsed = true; | |
| 104 | |||
| 105 | ✗ | ESP_LOGI(TAG, "Ticket validated and used: ID=%lu", ticketId); | |
| 106 | |||
| 107 | ✗ | xSemaphoreGive(m_mutex); | |
| 108 | ✗ | return true; | |
| 109 | } | ||
| 110 | |||
| 111 | ✗ | return false; | |
| 112 | } | ||
| 113 | |||
| 114 | ✗ | bool TicketService::getTicketInfo(uint32_t ticketId, Ticket& ticket) const { | |
| 115 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 116 | ✗ | auto it = m_tickets.find(ticketId); | |
| 117 | ✗ | if (it != m_tickets.end()) { | |
| 118 | ✗ | ticket = it->second; | |
| 119 | ✗ | xSemaphoreGive(m_mutex); | |
| 120 | ✗ | return true; | |
| 121 | } | ||
| 122 | ✗ | xSemaphoreGive(m_mutex); | |
| 123 | } | ||
| 124 | |||
| 125 | ✗ | return false; | |
| 126 | } | ||
| 127 | |||
| 128 | ✗ | uint32_t TicketService::getActiveTicketCount() const { | |
| 129 | ✗ | uint32_t count = 0; | |
| 130 | |||
| 131 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 132 | ✗ | for (const auto& [id, ticket] : m_tickets) { | |
| 133 | ✗ | if (!ticket.isUsed) { | |
| 134 | ✗ | count++; | |
| 135 | } | ||
| 136 | } | ||
| 137 | ✗ | xSemaphoreGive(m_mutex); | |
| 138 | } | ||
| 139 | |||
| 140 | ✗ | return count; | |
| 141 | } | ||
| 142 | |||
| 143 | ✗ | uint32_t TicketService::getCapacity() const { | |
| 144 | ✗ | return m_capacity; | |
| 145 | } | ||
| 146 | |||
| 147 | ✗ | void TicketService::reset() { | |
| 148 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 149 | ✗ | m_tickets.clear(); | |
| 150 | ✗ | m_nextTicketId = 1; | |
| 151 | ✗ | ESP_LOGI(TAG, "TicketService reset: all tickets cleared"); | |
| 152 | ✗ | xSemaphoreGive(m_mutex); | |
| 153 | } | ||
| 154 | ✗ | } | |
| 155 | |||
| 156 | ✗ | void TicketService::setCapacity(uint32_t capacity) { | |
| 157 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 158 | ✗ | m_capacity = capacity; | |
| 159 | ✗ | ESP_LOGI(TAG, "Capacity set to %lu", capacity); | |
| 160 | ✗ | xSemaphoreGive(m_mutex); | |
| 161 | } | ||
| 162 | ✗ | } | |
| 163 |