/* File: DataStore.cpp Description: Implements the DataStore class which provides a centralized singleton repository for managing system data in the Vehicle Service Management System. Includes accessors for users, services, combo packages, service bookings, job cards, inventory items, invoices, and payments. Author: Trenser Date: 19-May-2026 */ #include "DataStore.h" #include "Config.h" #include "SerializedRecords.h" #include "FileHelper.h" #include "ServiceBooking.h" #include "JobCard.h" #include "Invoice.h" /* Function: DataStore Description: Constructs the DataStore singleton and initializes internal handles to their default values. Parameters: - None Returns: - None */ DataStore::DataStore() : m_globalMutex(NULL) {} /* Function: ~DataStore Description: Destroys the DataStore singleton and releases all cached application objects owned by the datastore. This includes users, notifications, services, combo packages, inventory items, service bookings, job cards, and invoices. Parameters: - None Returns: - None */ DataStore::~DataStore() { clearCache(m_userCache); clearCache(m_notificationCache); clearCache(m_serviceCache); clearCache(m_comboPackageCache); clearCache(m_inventoryItemCache); clearCache(m_serviceBookingCache); clearCache(m_jobCardCache); clearCache(m_invoiceCache); } /* Function: initialize Description: Initializes the shared-memory datastore. Creates or opens the global datastore mutex, configures all mapping metadata, and creates or opens the file mappings used to persist application data. After successful completion, all datastore files are mapped into memory and ready for use by the application. Parameter: None Return type: bool - true : Initialization completed successfully. - false : Failed to create the mutex or open one or more file mappings. */ bool DataStore::initialize() { m_globalMutex = CreateMutexA(NULL, FALSE, "VehicleServiceSystemMutex"); if (m_globalMutex == NULL) { return false; } if (!lockDataStore()) { CloseHandle(m_globalMutex); m_globalMutex = NULL; return false; } bool success = true; do { util::ensureDirectoryExists(config::file::DIRECTORY); m_users.fileName = config::file::USER_FILE; m_users.recordSize = sizeof(SerializedUser); m_notifications.fileName = config::file::NOTIFICATION_FILE; m_notifications.recordSize = sizeof(SerializedNotification); m_services.fileName = config::file::SERVICE_FILE; m_services.recordSize = sizeof(SerializedService); m_comboPackages.fileName = config::file::COMBOPACKAGE_FILE; m_comboPackages.recordSize = sizeof(SerializedComboPackage); m_inventoryItems.fileName = config::file::INVENTORYITEM_FILE; m_inventoryItems.recordSize = sizeof(SerializedInventoryItem); m_serviceBookings.fileName = config::file::SERVICEBOOKING_FILE; m_serviceBookings.recordSize = sizeof(SerializedServiceBooking); m_jobCards.fileName = config::file::JOBCARD_FILE; m_jobCards.recordSize = sizeof(SerializedJobCard); m_invoices.fileName = config::file::INVOICE_FILE; m_invoices.recordSize = sizeof(SerializedInvoice); m_serviceManagementObservers.fileName = config::file::SERVICEMANAGEMENTOBSERVERS; m_serviceManagementObservers.recordSize = sizeof(SerializedObserver); m_paymentManagementObservers.fileName = config::file::PAYMENTMANAGEMENTOBSERVERS; m_paymentManagementObservers.recordSize = sizeof(SerializedObserver); m_inventoryManagementObservers.fileName = config::file::INVENTORYMANAGEMENTOBSERVERS; m_inventoryManagementObservers.recordSize = sizeof(SerializedObserver); if (!SharedMemory::createOrOpenMapping(m_users)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_notifications)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_services)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_comboPackages)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_inventoryItems)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_serviceBookings)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_jobCards)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_invoices)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_serviceManagementObservers)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_paymentManagementObservers)) { success = false; break; } if (!SharedMemory::createOrOpenMapping(m_inventoryManagementObservers)) { success = false; break; } } while (false); unlockDataStore(); if (!success) { shutdown(); } return success; } /* Function: shutdown Description: Releases all shared-memory resources owned by the datastore. Closes every file mapping, unmaps all mapped views, and releases the datastore mutex. After this call, the datastore must be initialized again before use. Parameter: None Return type: void */ void DataStore::shutdown() { SharedMemory::closeMapping(m_users); SharedMemory::closeMapping(m_notifications); SharedMemory::closeMapping(m_services); SharedMemory::closeMapping(m_comboPackages); SharedMemory::closeMapping(m_inventoryItems); SharedMemory::closeMapping(m_serviceBookings); SharedMemory::closeMapping(m_jobCards); SharedMemory::closeMapping(m_invoices); SharedMemory::closeMapping(m_serviceManagementObservers); SharedMemory::closeMapping(m_paymentManagementObservers); SharedMemory::closeMapping(m_inventoryManagementObservers); if (m_globalMutex != NULL) { CloseHandle(m_globalMutex); m_globalMutex = NULL; } } /* Function: getInstance Description: Provides a singleton instance of the DataStore class. Parameters: - None Returns: - Reference to the single DataStore instance. */ DataStore& DataStore::getInstance() { static DataStore dataStore; return dataStore; } /* Function: getUsers Description: Retrieves all user records from the datastore. Parameters: - None Returns: - util::Map>: Collection of user records */ util::Map>& DataStore::getUsers() { auto users = loadRecords(m_users); refreshCache(m_userCache, users); return m_userCache; } /* Function: getNotifications Description: Retrieves all notification records from the datastore. Parameters: - None Returns: - util::Map>: Collection of notification records */ util::Map>& DataStore::getNotifications() { auto notifications = loadRecords(m_notifications); refreshCache(m_notificationCache, notifications); return m_notificationCache; } /* Function: getServices Description: Retrieves all service records from the datastore. Parameters: - None Returns: - util::Map>: Collection of service records */ util::Map>& DataStore::getServices() { util::Map> services = loadRecords(m_services); refreshCache(m_serviceCache, services); util::Map>& inventoryItems = getInventoryItems(); size_t numberOfServices = m_serviceCache.getSize(); for (int iteratorOne =0; iteratorOne < numberOfServices; iteratorOne++) { Service* currentService = m_serviceCache.getValueAt(iteratorOne).data; util::Map inventoryItemMap; util::Vector currentServiceInventoryItem = currentService->getRequiredInventoryItemIDs(); for (int iteratorTwo = 0; iteratorTwo < currentServiceInventoryItem.getSize(); iteratorTwo++) { const std::string& currentInventoryItemId = currentServiceInventoryItem[iteratorTwo]; int currentInventoryItemIndex = inventoryItems.find(currentInventoryItemId); if (currentInventoryItemIndex == -1) { throw std::runtime_error("Invalid inventory item ID"); } InventoryItem* currentItem = inventoryItems.getValueAt(currentInventoryItemIndex).data; inventoryItemMap[currentInventoryItemId] = currentItem; } currentService->setRequiredInventoryItems(inventoryItemMap); } return m_serviceCache; } /* Function: getComboPackages Description: Retrieves all combo package records from the datastore. Parameters: - None Returns: - util::Map>: Collection of combo package records */ util::Map>& DataStore::getComboPackages() { util::Map> comboPackages = loadRecords(m_comboPackages); refreshCache(m_comboPackageCache, comboPackages); util::Map>& services = getServices(); size_t numberOfComboPackages = m_comboPackageCache.getSize(); for (int iteratorOne = 0; iteratorOne < numberOfComboPackages; iteratorOne++) { ComboPackage* currentComboPackage = m_comboPackageCache.getValueAt(iteratorOne).data; util::Vector currentServiceIds = currentComboPackage->getServiceIDs(); util::Map currentComboPackageServices; for (int iteratorTwo = 0; iteratorTwo < currentServiceIds.getSize(); iteratorTwo++) { const std::string& currentServiceId = currentServiceIds[iteratorTwo]; int serviceIndex = services.find(currentServiceId); if (serviceIndex == -1) { throw std::runtime_error("Invalid service ID"); } Service* currentService = services.getValueAt(serviceIndex).data; currentComboPackageServices[currentServiceId] = currentService; } currentComboPackage->setServices(currentComboPackageServices); } return m_comboPackageCache; } /* Function: getInventoryItems Description: Retrieves all inventory item records from the datastore. Parameters: - None Returns: - util::Map>: Collection of inventory item records */ util::Map>& DataStore::getInventoryItems() { auto inventoryItems = loadRecords(m_inventoryItems); refreshCache(m_inventoryItemCache, inventoryItems); return m_inventoryItemCache; } /* Function: getServiceBookings Description: Retrieves all service booking records from the datastore. Parameters: - None Returns: - util::Map>: Collection of service booking records */ util::Map>& DataStore::getServiceBookings() { util::Map> serviceBookings = loadRecords(m_serviceBookings); refreshCache(m_serviceBookingCache, serviceBookings); auto& users = getUsers(); auto& services = getServices(); size_t numberOfServiceBookings = m_serviceBookingCache.getSize(); for (int iteratorOne = 0; iteratorOne < numberOfServiceBookings; iteratorOne++) { ServiceBooking* serviceBooking = m_serviceBookingCache.getValueAt(iteratorOne).data; auto& serviceIds = serviceBooking->getServiceIDs(); util::Map servicesInBooking; for (int iteratorTwo = 0; iteratorTwo < serviceIds.getSize(); iteratorTwo++) { const std::string& currentServiceId = serviceIds[iteratorTwo]; int serviceIndex = services.find(currentServiceId); if (serviceIndex == -1) { throw std::runtime_error("Invalid service index."); } auto currentService = services.getValueAt(serviceIndex); servicesInBooking[currentServiceId] = currentService.data; } serviceBooking->setServices(servicesInBooking); if (!serviceBooking->getCustomerId().empty()) { int userIndex = users.find(serviceBooking->getCustomerId()); if (userIndex == -1) { throw std::runtime_error("Invalid user index."); } auto customer = users.getValueAt(userIndex); serviceBooking->setCustomer(customer.data); } if (!serviceBooking->getAssignedTechnicianId().empty()) { int technicianIndex = users.find(serviceBooking->getAssignedTechnicianId()); if (technicianIndex == -1) { throw std::runtime_error("Invalid technician index."); } auto technician = users.getValueAt(technicianIndex); serviceBooking->setAssignedTechnician(technician.data); } } return m_serviceBookingCache; } /* Function: getJobCards Description: Retrieves all job card records from the datastore. Parameters: - None Returns: - util::Map>: Collection of job card records */ util::Map>& DataStore::getJobCards() { util::Map> jobCards = loadRecords(m_jobCards); refreshCache(m_jobCardCache, jobCards); auto& serviceBookings = getServiceBookings(); auto& services = getServices(); auto& users = getUsers(); int numberOfJobCards = m_jobCardCache.getSize(); for (int iterator = 0; iterator < numberOfJobCards; iterator++) { JobCard* jobCard = m_jobCardCache.getValueAt(iterator).data; if (!jobCard) { continue; } const std::string& bookingId = jobCard->getBookingId(); int bookingIndex = serviceBookings.find(bookingId); if (bookingIndex == -1) { throw std::runtime_error("Invalid booking ID: " + bookingId); } auto trackedBooking = serviceBookings.getValueAt(bookingIndex); jobCard->setBooking(trackedBooking.data); const std::string& serviceId = jobCard->getServiceId(); int serviceIndex = services.find(serviceId); if (serviceIndex == -1) { throw std::runtime_error("Invalid service ID: " + serviceId); } auto trackedService = services.getValueAt(serviceIndex); jobCard->setService(trackedService.data); const std::string& technicianId = jobCard->getTechnicianId(); if (!technicianId.empty()) { int technicianIndex = users.find(technicianId); if (technicianIndex == -1) { throw std::runtime_error("Invalid technician ID: " + technicianId); } auto trackedTechnician = users.getValueAt(technicianIndex); jobCard->setTechnician(trackedTechnician.data); } } return m_jobCardCache; } /* Function: getInvoices Description: Retrieves all invoice records from the datastore. Parameters: - None Returns: - util::Map>: Collection of invoice records */ util::Map>& DataStore::getInvoices() { auto& serviceBookings = getServiceBookings(); auto& inventoryItems = getInventoryItems(); util::Map> invoices = loadRecords(m_invoices); refreshCache(m_invoiceCache, invoices); for (int iterator = 0; iterator < m_invoiceCache.getSize(); iterator++) { auto& trackedInvoice = m_invoiceCache.getValueAt(iterator); Invoice* invoice = trackedInvoice.data; if (!invoice) { continue; } const std::string& currentBookingId = invoice->getBookingId(); int currentBookingIndex = serviceBookings.find(currentBookingId); if (currentBookingIndex == -1) { throw std::runtime_error("Invalid Service Booking Index."); } ServiceBooking* currentBooking = serviceBookings.getValueAt(currentBookingIndex).data; auto& currentInventoryItemIds = invoice->getPartIDs(); util::Map currentInventoryItems; for (int iterator = 0; iterator < currentInventoryItemIds.getSize(); iterator++) { const std::string& currentItemId = currentInventoryItemIds[iterator]; int currentItemIndex = inventoryItems.find(currentItemId); if (currentItemIndex == -1) { throw std::runtime_error("Invalid Inventory item id."); } InventoryItem* currentItem = inventoryItems.getValueAt(currentItemIndex).data; if (!currentItem) { continue; } currentInventoryItems[currentItemId] = currentItem; } invoice->setBooking(currentBooking); invoice->setParts(currentInventoryItems); } return m_invoiceCache; } /* Function: getObservers Description: Retrieves observer records from the specified observer mapping and resolves them to User objects. Parameters: - mapping: Observer mapping to read from Returns: - util::Map: Collection of observer records Throws: - std::runtime_error if an observer references an invalid user ID */ util::Map DataStore::getObservers(MappingInfo& mapping) { auto& users = getUsers(); util::Map observers; SharedMemory::ensureLatestMapping(mapping); size_t recordCount = SharedMemory::getRecordCount(mapping); for (size_t index = 0; index < recordCount; index++) { const SerializedObserver* observer = static_cast(SharedMemory::getRecordAddress(mapping, index)); int userIndex = users.find(observer->id); if (userIndex == -1) { throw std::runtime_error("Invalid observer user ID"); } User* user = users.getValueAt(userIndex).data; observers.insert(user->getId(), user); } return observers; } /* Function: getServiceManagementObservers Description: Retrieves all service management observer records from the datastore. Parameters: - None Returns: - util::Map: Collection of observer records */ util::Map DataStore::getServiceManagementObservers() { return getObservers(m_serviceManagementObservers); } /* Function: getPaymentManagementObservers Description: Retrieves all payment management observer records from the datastore. Parameters: - None Returns: - util::Map: Collection of observer records */ util::Map DataStore::getPaymentManagementObservers() { return getObservers(m_paymentManagementObservers); } /* Function: getInventoryManagementObservers Description: Retrieves all inventory management observer records from the datastore. Parameters: - None Returns: - util::Map: Collection of observer records */ util::Map DataStore::getInventoryManagementObservers() { return getObservers(m_inventoryManagementObservers); } /* Function: saveUsers Description: Persists all user records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveUsers() { saveRecords(m_users, m_userCache); } /* Function: saveNotifications Description: Persists all notification records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveNotifications() { saveRecords(m_notifications, m_notificationCache); } /* Function: saveServices Description: Persists all service records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveServices() { saveRecords(m_services, m_serviceCache); } /* Function: saveComboPackages Description: Persists all combo package records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveComboPackages() { saveRecords(m_comboPackages, m_comboPackageCache); } /* Function: saveInventoryItems Description: Persists all inventory item records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveInventoryItems() { saveRecords(m_inventoryItems, m_inventoryItemCache); } /* Function: saveServiceBookings Description: Persists all service booking records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveServiceBookings() { saveRecords(m_serviceBookings, m_serviceBookingCache); } /* Function: saveJobCards Description: Persists all job card records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveJobCards() { saveRecords(m_jobCards, m_jobCardCache); } /* Function: saveInvoices Description: Persists all invoice records to the datastore. Parameters: - None Returns: - None */ void DataStore::saveInvoices() { saveRecords(m_invoices, m_invoiceCache); } /* Function: saveObservers Description: Persists observer records to the specified observer mapping. Parameters: - mapping: MappingInfo&, observer mapping to save to - observers: util::Map&, collection of observer records Returns: - None */ void DataStore::saveObservers(MappingInfo& mapping, util::Map& observers) { size_t observerCount = static_cast(observers.getSize()); size_t capacity = config::file::INITIAL_CAPACITY; while (capacity < observerCount) { capacity *= config::file::GROWTH_FACTOR; } if (!SharedMemory::resizeMapping(mapping, capacity)) { throw std::runtime_error("Failed to resize observer mapping"); } SharedMemory::setRecordCount(mapping, observerCount); for (size_t index = 0; index < observerCount; index++) { SerializedObserver serializedObserver; User* user = observers.getValueAt(static_cast(index)); strcpy_s(serializedObserver.id, sizeof(serializedObserver.id), user->getId().c_str()); SerializedObserver* destination = static_cast(SharedMemory::getRecordAddress(mapping, index)); *destination = serializedObserver; } } /* Function: saveServiceManagementObservers Description: Persists all service management observer records to the datastore. Parameters: - observers: util::Map&, collection of observer records Returns: - None */ void DataStore::saveServiceManagementObservers(util::Map& observers) { saveObservers(m_serviceManagementObservers, observers); } /* Function: savePaymentManagementObservers Description: Persists all payment management observer records to the datastore. Parameters: - observers: util::Map&, collection of observer records Returns: - None */ void DataStore::savePaymentManagementObservers(util::Map& observers) { saveObservers(m_paymentManagementObservers, observers); } /* Function: saveInventoryManagementObservers Description: Persists all inventory management observer records to the datastore. Parameters: - observers: util::Map&, collection of observer records Returns: - None */ void DataStore::saveInventoryManagementObservers(util::Map& observers) { saveObservers(m_inventoryManagementObservers, observers); } /* Function: lockDataStore Description: Acquires the datastore mutex, providing exclusive access to the shared-memory datastore. This function blocks until the mutex becomes available or an error occurs. Parameter: None Return type: bool - true : Mutex successfully acquired. - false : Failed to acquire the mutex. */ bool DataStore::lockDataStore() { if (m_globalMutex == NULL) { return false; } DWORD result = WaitForSingleObject(m_globalMutex, INFINITE); return result == WAIT_OBJECT_0; } /* Function: unlockDataStore Description: Releases the datastore mutex after a successful call to lockDataStore(), allowing other processes to access the shared-memory datastore. Parameter: None Return type: bool - true : Mutex successfully released. - false : Failed to release the mutex. */ bool DataStore::unlockDataStore() { if (m_globalMutex == NULL) { return false; } return ReleaseMutex(m_globalMutex) != 0; }