Files

783 lines
25 KiB
C++

/*
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<std::string, TrackedRecord<User>>: Collection of user records
*/
util::Map<std::string, TrackedRecord<User>>& DataStore::getUsers()
{
auto users = loadRecords<User, SerializedUser>(m_users);
refreshCache(m_userCache, users);
return m_userCache;
}
/*
Function: getNotifications
Description: Retrieves all notification records from the datastore.
Parameters:
- None
Returns:
- util::Map<std::string, TrackedRecord<Notification>>: Collection of notification records
*/
util::Map<std::string, TrackedRecord<Notification>>& DataStore::getNotifications()
{
auto notifications = loadRecords<Notification, SerializedNotification>(m_notifications);
refreshCache(m_notificationCache, notifications);
return m_notificationCache;
}
/*
Function: getServices
Description: Retrieves all service records from the datastore.
Parameters:
- None
Returns:
- util::Map<std::string, TrackedRecord<Service>>: Collection of service records
*/
util::Map<std::string, TrackedRecord<Service>>& DataStore::getServices()
{
util::Map<std::string, TrackedRecord<Service>> services = loadRecords<Service, SerializedService>(m_services);
refreshCache(m_serviceCache, services);
util::Map<std::string, TrackedRecord<InventoryItem>>& inventoryItems = getInventoryItems();
size_t numberOfServices = m_serviceCache.getSize();
for (int iteratorOne =0; iteratorOne < numberOfServices; iteratorOne++)
{
Service* currentService = m_serviceCache.getValueAt(iteratorOne).data;
util::Map<std::string, InventoryItem*> inventoryItemMap;
util::Vector<std::string> 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<std::string, TrackedRecord<ComboPackage>>: Collection of combo package records
*/
util::Map<std::string, TrackedRecord<ComboPackage>>& DataStore::getComboPackages()
{
util::Map<std::string, TrackedRecord<ComboPackage>> comboPackages = loadRecords<ComboPackage, SerializedComboPackage>(m_comboPackages);
refreshCache(m_comboPackageCache, comboPackages);
util::Map<std::string, TrackedRecord<Service>>& services = getServices();
size_t numberOfComboPackages = m_comboPackageCache.getSize();
for (int iteratorOne = 0; iteratorOne < numberOfComboPackages; iteratorOne++)
{
ComboPackage* currentComboPackage = m_comboPackageCache.getValueAt(iteratorOne).data;
util::Vector<std::string> currentServiceIds = currentComboPackage->getServiceIDs();
util::Map<std::string, Service*> 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<std::string, TrackedRecord<InventoryItem>>: Collection of inventory item records
*/
util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems()
{
auto inventoryItems = loadRecords<InventoryItem, SerializedInventoryItem>(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<std::string, TrackedRecord<ServiceBooking>>: Collection of service booking records
*/
util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBookings()
{
util::Map<std::string, TrackedRecord<ServiceBooking>> serviceBookings = loadRecords<ServiceBooking, SerializedServiceBooking>(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<std::string, Service*> 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<std::string, TrackedRecord<JobCard>>: Collection of job card records
*/
util::Map<std::string, TrackedRecord<JobCard>>& DataStore::getJobCards()
{
util::Map<std::string, TrackedRecord<JobCard>> jobCards = loadRecords<JobCard, SerializedJobCard>(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<std::string, TrackedRecord<Invoice>>: Collection of invoice records
*/
util::Map<std::string, TrackedRecord<Invoice>>& DataStore::getInvoices()
{
auto& serviceBookings = getServiceBookings();
auto& inventoryItems = getInventoryItems();
util::Map<std::string, TrackedRecord<Invoice>> invoices = loadRecords<Invoice, SerializedInvoice>(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<std::string, InventoryItem*> 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<std::string, User*>: Collection of observer records
Throws:
- std::runtime_error if an observer references an invalid user ID
*/
util::Map<std::string, User*> DataStore::getObservers(MappingInfo& mapping)
{
auto& users = getUsers();
util::Map<std::string, User*> observers;
SharedMemory::ensureLatestMapping(mapping);
size_t recordCount = SharedMemory::getRecordCount(mapping);
for (size_t index = 0; index < recordCount; index++)
{
const SerializedObserver* observer = static_cast<SerializedObserver*>(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<std::string, User*>: Collection of observer records
*/
util::Map<std::string, User*> DataStore::getServiceManagementObservers()
{
return getObservers(m_serviceManagementObservers);
}
/*
Function: getPaymentManagementObservers
Description: Retrieves all payment management observer records from the datastore.
Parameters:
- None
Returns:
- util::Map<std::string, User*>: Collection of observer records
*/
util::Map<std::string, User*> DataStore::getPaymentManagementObservers()
{
return getObservers(m_paymentManagementObservers);
}
/*
Function: getInventoryManagementObservers
Description: Retrieves all inventory management observer records from the datastore.
Parameters:
- None
Returns:
- util::Map<std::string, User*>: Collection of observer records
*/
util::Map<std::string, User*> DataStore::getInventoryManagementObservers()
{
return getObservers(m_inventoryManagementObservers);
}
/*
Function: saveUsers
Description: Persists all user records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveUsers()
{
saveRecords<User, SerializedUser>(m_users, m_userCache);
}
/*
Function: saveNotifications
Description: Persists all notification records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveNotifications()
{
saveRecords<Notification, SerializedNotification>(m_notifications, m_notificationCache);
}
/*
Function: saveServices
Description: Persists all service records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveServices()
{
saveRecords<Service, SerializedService>(m_services, m_serviceCache);
}
/*
Function: saveComboPackages
Description: Persists all combo package records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveComboPackages()
{
saveRecords<ComboPackage, SerializedComboPackage>(m_comboPackages, m_comboPackageCache);
}
/*
Function: saveInventoryItems
Description: Persists all inventory item records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveInventoryItems()
{
saveRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems, m_inventoryItemCache);
}
/*
Function: saveServiceBookings
Description: Persists all service booking records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveServiceBookings()
{
saveRecords<ServiceBooking, SerializedServiceBooking>(m_serviceBookings, m_serviceBookingCache);
}
/*
Function: saveJobCards
Description: Persists all job card records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveJobCards()
{
saveRecords<JobCard, SerializedJobCard>(m_jobCards, m_jobCardCache);
}
/*
Function: saveInvoices
Description: Persists all invoice records to the datastore.
Parameters:
- None
Returns:
- None
*/
void DataStore::saveInvoices()
{
saveRecords<Invoice, SerializedInvoice>(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<std::string, User*>&, collection of observer records
Returns:
- None
*/
void DataStore::saveObservers(MappingInfo& mapping, util::Map<std::string, User*>& observers)
{
size_t observerCount = static_cast<size_t>(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<int>(index));
strcpy_s(serializedObserver.id, sizeof(serializedObserver.id), user->getId().c_str());
SerializedObserver* destination = static_cast<SerializedObserver*>(SharedMemory::getRecordAddress(mapping, index));
*destination = serializedObserver;
}
}
/*
Function: saveServiceManagementObservers
Description: Persists all service management observer records to the datastore.
Parameters:
- observers: util::Map<std::string, User*>&, collection of observer records
Returns:
- None
*/
void DataStore::saveServiceManagementObservers(util::Map<std::string, User*>& observers)
{
saveObservers(m_serviceManagementObservers, observers);
}
/*
Function: savePaymentManagementObservers
Description: Persists all payment management observer records to the datastore.
Parameters:
- observers: util::Map<std::string, User*>&, collection of observer records
Returns:
- None
*/
void DataStore::savePaymentManagementObservers(util::Map<std::string, User*>& observers)
{
saveObservers(m_paymentManagementObservers, observers);
}
/*
Function: saveInventoryManagementObservers
Description: Persists all inventory management observer records to the datastore.
Parameters:
- observers: util::Map<std::string, User*>&, collection of observer records
Returns:
- None
*/
void DataStore::saveInventoryManagementObservers(util::Map<std::string, User*>& 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;
}