1+ // consumer.c
2+ #include <stdio.h>
3+ #include <stdlib.h>
4+ #include <unistd.h>
5+ #include <fcntl.h>
6+ #include <sys/mman.h>
7+ #include <sys/stat.h>
8+ #include <semaphore.h>
9+ #include <time.h>
10+ #include <string.h>
11+ #include <float.h> // For FLT_MAX, FLT_MIN (or DBL_MAX etc.)
12+ #include <signal.h> // For signal handling
13+
14+ #include "data_structures.h" // Common header
15+
16+ SharedData * shared_data_ptr = NULL ;
17+ int shm_fd = -1 ;
18+ sem_t * mutex_sem = SEM_FAILED ;
19+ volatile sig_atomic_t keep_running = 1 ;
20+
21+ void cleanup_ipc_consumer () {
22+ printf ("\nConsumer cleaning up...\n" );
23+ if (shared_data_ptr != MAP_FAILED && shared_data_ptr != NULL ) {
24+ if (munmap (shared_data_ptr , sizeof (SharedData )) == -1 ) {
25+ perror ("munmap" );
26+ }
27+ }
28+ if (shm_fd != -1 ) {
29+ close (shm_fd );
30+ // Consumer does NOT unlink shared memory, producer owns it
31+ }
32+ if (mutex_sem != SEM_FAILED ) {
33+ sem_close (mutex_sem );
34+ // Consumer does NOT unlink semaphore, producer owns it
35+ }
36+ printf ("Consumer cleanup complete.\n" );
37+ }
38+
39+ void sigint_handler_consumer (int sig ) {
40+ printf ("\nConsumer received SIGINT. Shutting down...\n" );
41+ keep_running = 0 ;
42+ }
43+
44+ int main () {
45+ struct sigaction sa ;
46+ sa .sa_handler = sigint_handler_consumer ;
47+ sigemptyset (& sa .sa_mask );
48+ sa .sa_flags = 0 ;
49+ if (sigaction (SIGINT , & sa , NULL ) == -1 ) {
50+ perror ("sigaction" );
51+ return EXIT_FAILURE ;
52+ }
53+ if (sigaction (SIGTERM , & sa , NULL ) == -1 ) {
54+ perror ("sigaction for SIGTERM" );
55+ return EXIT_FAILURE ;
56+ }
57+
58+ // 1. Open existing POSIX shared memory object (DO NOT CREATE)
59+ shm_fd = shm_open (SHM_NAME , O_RDWR , 0666 ); // O_RDONLY if only reading
60+ if (shm_fd == -1 ) {
61+ perror ("shm_open (Is the producer.c program running?)" );
62+ return EXIT_FAILURE ;
63+ }
64+
65+ // 2. Map the shared memory object into the process's address space
66+ // Note: Size is known from SharedData struct. ftruncate is not needed here.
67+ shared_data_ptr = mmap (0 , sizeof (SharedData ), PROT_READ | PROT_WRITE , MAP_SHARED , shm_fd , 0 ); // PROT_READ if only reading
68+ if (shared_data_ptr == MAP_FAILED ) {
69+ perror ("mmap" );
70+ close (shm_fd );
71+ return EXIT_FAILURE ;
72+ }
73+ printf ("Shared memory opened and mapped successfully.\n" );
74+
75+ // 3. Open existing POSIX named semaphore (DO NOT CREATE)
76+ mutex_sem = sem_open (SEM_MUTEX_NAME , 0 ); // Flags argument is 0 when opening existing
77+ if (mutex_sem == SEM_FAILED ) {
78+ perror ("sem_open (Is the producer.c program running and semaphore created?)" );
79+ munmap (shared_data_ptr , sizeof (SharedData ));
80+ close (shm_fd );
81+ return EXIT_FAILURE ;
82+ }
83+ printf ("Mutex semaphore opened successfully.\n" );
84+
85+ printf ("Monitoring room display started. Press Ctrl+C to exit.\n" );
86+ printf ("Will display data every %d seconds.\n\n" , MONITOR_DISPLAY_INTERVAL );
87+
88+ // 4. Monitoring loop
89+ while (keep_running ) {
90+ // Wait for semaphore
91+ if (sem_wait (mutex_sem ) == -1 ) {
92+ if (keep_running )
93+ perror ("sem_wait in consumer" ); // Don't print error if shutting down
94+ break ; // Exit loop on error or interruption
95+ }
96+
97+ float max_temp = - FLT_MAX ;
98+ float min_temp = FLT_MAX ;
99+ int hottest_workshop_idx = -1 ;
100+ int coldest_workshop_idx = -1 ;
101+ int valid_data_found = 0 ;
102+
103+ for (int i = 0 ; i < NUM_WORKSHOPS ; ++ i ) {
104+ // Check if the workshop data looks initialized (not default 0.0 or some other sentinel)
105+ // For this example, any non-zero temp can be considered, or rely on producer to fill.
106+ // A more robust way would be a 'valid' flag per workshop or timestamp.
107+ if (shared_data_ptr -> workshops [i ].temperature != 0.0f || shared_data_ptr -> workshops [i ].humidity != 0.0f ) {
108+ valid_data_found = 1 ; // At least one workshop has some data
109+ }
110+
111+ float current_temp = shared_data_ptr -> workshops [i ].temperature ;
112+
113+ if (current_temp > max_temp ) {
114+ max_temp = current_temp ;
115+ hottest_workshop_idx = i ;
116+ }
117+ if (current_temp < min_temp ) {
118+ min_temp = current_temp ;
119+ coldest_workshop_idx = i ;
120+ }
121+ }
122+
123+ // Release semaphore
124+ if (sem_post (mutex_sem ) == -1 ) {
125+ perror ("sem_post in consumer" );
126+ break ; // Exit loop on error
127+ }
128+
129+ // Display data
130+ if (hottest_workshop_idx != -1 && coldest_workshop_idx != -1 && valid_data_found ) {
131+ printf ("--- Monitoring Update (%s" , ctime (& (time_t ){ time (NULL ) })); // ctime adds newline
132+ printf (" Highest Temp: Workshop %d (%.2f C, %.2f %% Humidity)\n" ,
133+ shared_data_ptr -> workshops [hottest_workshop_idx ].workshop_id ,
134+ max_temp ,
135+ shared_data_ptr -> workshops [hottest_workshop_idx ].humidity );
136+ printf (" Lowest Temp: Workshop %d (%.2f C, %.2f %% Humidity)\n" ,
137+ shared_data_ptr -> workshops [coldest_workshop_idx ].workshop_id ,
138+ min_temp ,
139+ shared_data_ptr -> workshops [coldest_workshop_idx ].humidity );
140+ printf ("---\n\n" );
141+ }
142+ else if (!valid_data_found ) {
143+ printf ("[%s] Waiting for initial data from workshops...\n\n" , ctime (& (time_t ){ time (NULL ) }));
144+ }
145+ else {
146+ printf ("[%s] No valid temperature extremes found yet (all workshops might have same temp or no data).\n\n" , ctime (& (time_t ){ time (NULL ) }));
147+ }
148+ fflush (stdout );
149+
150+ // Sleep for the display interval
151+ for (int i = 0 ; i < MONITOR_DISPLAY_INTERVAL && keep_running ; ++ i ) {
152+ sleep (1 );
153+ }
154+ }
155+
156+ // 5. Cleanup: Unmap shared memory, close file descriptor, close semaphore
157+ cleanup_ipc_consumer ();
158+
159+ return EXIT_SUCCESS ;
160+ }
0 commit comments