smart_message 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +38 -0
  3. data/.gitignore +5 -0
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile.lock +35 -4
  6. data/README.md +169 -71
  7. data/Rakefile +29 -4
  8. data/docs/assets/images/ddq_architecture.svg +130 -0
  9. data/docs/assets/images/dlq_architecture.svg +115 -0
  10. data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
  11. data/docs/assets/images/enhanced-fluent-api.svg +149 -0
  12. data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
  13. data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
  14. data/docs/assets/images/fluent-api-demo.svg +59 -0
  15. data/docs/assets/images/performance-comparison.svg +161 -0
  16. data/docs/assets/images/redis-basic-architecture.svg +53 -0
  17. data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
  18. data/docs/assets/images/redis-queue-architecture.svg +101 -0
  19. data/docs/assets/images/smart_message.jpg +0 -0
  20. data/docs/assets/images/smart_message_walking.jpg +0 -0
  21. data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
  22. data/docs/assets/images/transport-comparison-matrix.svg +171 -0
  23. data/docs/assets/javascripts/mathjax.js +17 -0
  24. data/docs/assets/stylesheets/extra.css +51 -0
  25. data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
  26. data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
  27. data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
  28. data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
  29. data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
  30. data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
  31. data/docs/{examples.md → getting-started/examples.md} +115 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
  33. data/docs/guides/redis-queue-getting-started.md +697 -0
  34. data/docs/guides/redis-queue-patterns.md +889 -0
  35. data/docs/guides/redis-queue-production.md +1091 -0
  36. data/docs/index.md +64 -0
  37. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  38. data/docs/{logging.md → reference/logging.md} +1 -1
  39. data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
  40. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  41. data/docs/{serializers.md → reference/serializers.md} +3 -5
  42. data/docs/{transports.md → reference/transports.md} +133 -11
  43. data/docs/transports/memory-transport.md +374 -0
  44. data/docs/transports/redis-enhanced-transport.md +524 -0
  45. data/docs/transports/redis-queue-transport.md +1304 -0
  46. data/docs/transports/redis-transport-comparison.md +496 -0
  47. data/docs/transports/redis-transport.md +509 -0
  48. data/examples/README.md +98 -5
  49. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  50. data/examples/city_scenario/README.md +515 -0
  51. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  52. data/examples/city_scenario/citizen.rb +195 -0
  53. data/examples/city_scenario/city_diagram.svg +125 -0
  54. data/examples/city_scenario/common/health_monitor.rb +80 -0
  55. data/examples/city_scenario/common/logger.rb +30 -0
  56. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  57. data/examples/city_scenario/fire_department.rb +446 -0
  58. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  59. data/examples/city_scenario/health_department.rb +100 -0
  60. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  61. data/examples/city_scenario/house.rb +244 -0
  62. data/examples/city_scenario/local_bank.rb +217 -0
  63. data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
  64. data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
  65. data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
  66. data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
  67. data/examples/city_scenario/messages/health_check_message.rb +22 -0
  68. data/examples/city_scenario/messages/health_status_message.rb +35 -0
  69. data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
  70. data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
  71. data/examples/city_scenario/police_department.rb +316 -0
  72. data/examples/city_scenario/redis_monitor.rb +129 -0
  73. data/examples/city_scenario/redis_stats.rb +743 -0
  74. data/examples/city_scenario/room_for_improvement.md +240 -0
  75. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  76. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  77. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  78. data/examples/city_scenario/start_demo.sh +236 -0
  79. data/examples/city_scenario/stop_demo.sh +106 -0
  80. data/examples/city_scenario/visitor.rb +631 -0
  81. data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
  82. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
  83. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
  84. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
  85. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
  86. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  87. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
  88. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
  89. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
  90. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
  91. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
  92. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
  93. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
  94. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
  95. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
  96. data/examples/memory/README.md +163 -0
  97. data/examples/memory/memory_transport_architecture.svg +90 -0
  98. data/examples/memory/point_to_point_pattern.svg +94 -0
  99. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  100. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
  101. data/examples/redis/README.md +230 -0
  102. data/examples/redis/alert_system_flow.svg +127 -0
  103. data/examples/redis/dashboard_status_flow.svg +107 -0
  104. data/examples/redis/device_command_flow.svg +113 -0
  105. data/examples/redis/redis_transport_architecture.svg +115 -0
  106. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  107. data/examples/redis/smart_home_system_architecture.svg +133 -0
  108. data/examples/redis_enhanced/README.md +319 -0
  109. data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
  110. data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
  111. data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
  112. data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
  113. data/examples/redis_queue/01_basic_messaging.rb +221 -0
  114. data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
  115. data/examples/redis_queue/02_pattern_routing.rb +405 -0
  116. data/examples/redis_queue/03_fluent_api.rb +422 -0
  117. data/examples/redis_queue/04_load_balancing.rb +486 -0
  118. data/examples/redis_queue/05_microservices.rb +735 -0
  119. data/examples/redis_queue/06_emergency_alerts.rb +777 -0
  120. data/examples/redis_queue/07_queue_management.rb +587 -0
  121. data/examples/redis_queue/README.md +366 -0
  122. data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
  123. data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
  124. data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
  125. data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
  126. data/examples/redis_queue/redis_queue_architecture.svg +148 -0
  127. data/ideas/README.md +41 -0
  128. data/ideas/agents.md +1001 -0
  129. data/ideas/database_transport.md +980 -0
  130. data/ideas/improvement.md +359 -0
  131. data/ideas/meshage.md +1788 -0
  132. data/ideas/message_discovery.md +178 -0
  133. data/ideas/message_schema.md +1381 -0
  134. data/lib/smart_message/.idea/.gitignore +8 -0
  135. data/lib/smart_message/.idea/markdown.xml +6 -0
  136. data/lib/smart_message/.idea/misc.xml +4 -0
  137. data/lib/smart_message/.idea/modules.xml +8 -0
  138. data/lib/smart_message/.idea/smart_message.iml +16 -0
  139. data/lib/smart_message/.idea/vcs.xml +6 -0
  140. data/lib/smart_message/addressing.rb +15 -0
  141. data/lib/smart_message/base.rb +0 -2
  142. data/lib/smart_message/configuration.rb +1 -1
  143. data/lib/smart_message/logger.rb +15 -4
  144. data/lib/smart_message/plugins.rb +5 -2
  145. data/lib/smart_message/serializer.rb +14 -0
  146. data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
  147. data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
  148. data/lib/smart_message/transport/registry.rb +1 -0
  149. data/lib/smart_message/transport.rb +34 -1
  150. data/lib/smart_message/version.rb +1 -1
  151. data/lib/smart_message.rb +5 -52
  152. data/mkdocs.yml +184 -0
  153. data/p2p_plan.md +326 -0
  154. data/p2p_roadmap.md +287 -0
  155. data/smart_message.gemspec +2 -0
  156. data/smart_message.svg +51 -0
  157. metadata +170 -44
  158. data/docs/README.md +0 -57
  159. data/examples/dead_letters.jsonl +0 -12
  160. data/examples/temp.txt +0 -94
  161. data/examples/tmux_chat/README.md +0 -283
  162. data/examples/tmux_chat/bot_agent.rb +0 -278
  163. data/examples/tmux_chat/human_agent.rb +0 -199
  164. data/examples/tmux_chat/room_monitor.rb +0 -160
  165. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  166. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  167. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  168. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  169. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -0,0 +1,777 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/redis_queue/06_emergency_alerts.rb
3
+ # Emergency alert system with Redis Queue Transport
4
+
5
+ require_relative '../../lib/smart_message'
6
+
7
+ puts "🚨 Redis Queue Transport - Emergency Alert System Demo"
8
+ puts "=" * 60
9
+
10
+ #==============================================================================
11
+ # Emergency Alert System Configuration
12
+ #==============================================================================
13
+
14
+ SmartMessage.configure do |config|
15
+ config.transport = :redis_queue
16
+ config.transport_options = {
17
+ url: 'redis://localhost:6379',
18
+ db: 6, # Use database 6 for emergency alert demo
19
+ queue_prefix: 'emergency_alerts',
20
+ consumer_group: 'emergency_responders'
21
+ }
22
+ end
23
+
24
+ #==============================================================================
25
+ # Emergency Message Classes
26
+ #==============================================================================
27
+
28
+ class EmergencyAlert < SmartMessage::Base
29
+ transport :redis_queue
30
+
31
+ property :alert_id, required: true
32
+ property :alert_type, required: true # fire, medical, security, natural_disaster, infrastructure
33
+ property :severity, required: true # low, medium, high, critical
34
+ property :title, required: true
35
+ property :description, required: true
36
+ property :location, required: true
37
+ property :coordinates, default: {} # lat/lng
38
+ property :affected_area_radius, default: 0 # in kilometers
39
+ property :estimated_affected_people, default: 0
40
+ property :response_required, default: true
41
+ property :contact_info, default: {}
42
+ property :metadata, default: {}
43
+ property :timestamp, default: -> { Time.now }
44
+
45
+ def process
46
+ severity_icon = case severity
47
+ when 'critical' then '🔥'
48
+ when 'high' then '🚨'
49
+ when 'medium' then '⚠️'
50
+ when 'low' then '⚡'
51
+ else '📢'
52
+ end
53
+
54
+ type_icon = case alert_type
55
+ when 'fire' then '🔥'
56
+ when 'medical' then '🚑'
57
+ when 'security' then '👮'
58
+ when 'natural_disaster' then '🌪️'
59
+ when 'infrastructure' then '🏗️'
60
+ else '🚨'
61
+ end
62
+
63
+ puts "#{severity_icon}#{type_icon} ALERT #{alert_id} [#{severity.upcase}]: #{title}"
64
+ puts " Type: #{alert_type.upcase}"
65
+ puts " Location: #{location}"
66
+ puts " Description: #{description}"
67
+ puts " People affected: #{estimated_affected_people}" if estimated_affected_people > 0
68
+ puts " Response required: #{response_required ? 'YES' : 'NO'}"
69
+ end
70
+ end
71
+
72
+ class EmergencyResponse < SmartMessage::Base
73
+ transport :redis_queue
74
+
75
+ property :response_id, required: true
76
+ property :alert_id, required: true
77
+ property :responding_unit, required: true # fire_dept, police, hospital, utility_company
78
+ property :response_type, required: true # dispatched, on_scene, resolved, cancelled
79
+ property :eta_minutes
80
+ property :personnel_count, default: 0
81
+ property :equipment, default: []
82
+ property :status_update, required: true
83
+ property :timestamp, default: -> { Time.now }
84
+
85
+ def process
86
+ response_icon = case response_type
87
+ when 'dispatched' then '🚀'
88
+ when 'on_scene' then '📍'
89
+ when 'resolved' then '✅'
90
+ when 'cancelled' then '❌'
91
+ else '📝'
92
+ end
93
+
94
+ unit_icon = case responding_unit
95
+ when 'fire_dept' then '🚒'
96
+ when 'police' then '🚔'
97
+ when 'hospital' then '🚑'
98
+ when 'utility_company' then '⚡'
99
+ else '🚗'
100
+ end
101
+
102
+ puts "#{response_icon}#{unit_icon} RESPONSE #{response_id}: #{responding_unit.upcase} - #{response_type.upcase}"
103
+ puts " Alert ID: #{alert_id}"
104
+ puts " Status: #{status_update}"
105
+ puts " ETA: #{eta_minutes} minutes" if eta_minutes
106
+ puts " Personnel: #{personnel_count}" if personnel_count > 0
107
+ puts " Equipment: #{equipment.join(', ')}" if equipment.any?
108
+ end
109
+ end
110
+
111
+ class CitizenReport < SmartMessage::Base
112
+ transport :redis_queue
113
+
114
+ property :report_id, required: true
115
+ property :reporter_id
116
+ property :report_type, required: true # incident, tip, request_help, status_update
117
+ property :description, required: true
118
+ property :location, required: true
119
+ property :urgency, default: 'medium' # low, medium, high
120
+ property :contact_phone
121
+ property :photos, default: []
122
+ property :verified, default: false
123
+ property :timestamp, default: -> { Time.now }
124
+
125
+ def process
126
+ urgency_icon = case urgency
127
+ when 'high' then '🚨'
128
+ when 'medium' then '⚠️'
129
+ when 'low' then 'ℹ️'
130
+ else '📢'
131
+ end
132
+
133
+ puts "#{urgency_icon} CITIZEN REPORT #{report_id} [#{urgency.upcase}]: #{report_type.upcase}"
134
+ puts " Location: #{location}"
135
+ puts " Description: #{description}"
136
+ puts " Contact: #{contact_phone}" if contact_phone
137
+ puts " Photos: #{photos.size} attached" if photos.any?
138
+ puts " Verified: #{verified ? 'YES' : 'PENDING'}"
139
+ end
140
+ end
141
+
142
+ #==============================================================================
143
+ # Emergency Response Services
144
+ #==============================================================================
145
+
146
+ class EmergencyDispatchCenter
147
+ def initialize(transport)
148
+ @transport = transport
149
+ @active_alerts = {}
150
+ @response_units = {}
151
+ setup_subscriptions
152
+ end
153
+
154
+ def setup_subscriptions
155
+ # Listen for new citizen reports
156
+ @transport.where
157
+ .type('CitizenReport')
158
+ .to('dispatch_center')
159
+ .subscribe do |message_class, message_data|
160
+ handle_citizen_report(JSON.parse(message_data))
161
+ end
162
+
163
+ # Listen for response updates
164
+ @transport.where
165
+ .type('EmergencyResponse')
166
+ .to('dispatch_center')
167
+ .subscribe do |message_class, message_data|
168
+ track_response(JSON.parse(message_data))
169
+ end
170
+ end
171
+
172
+ def create_alert(alert_type, severity, title, description, location, options = {})
173
+ alert_id = "ALERT-#{Time.now.strftime('%Y%m%d')}-#{sprintf('%04d', rand(9999))}"
174
+
175
+ alert = EmergencyAlert.new({
176
+ alert_id: alert_id,
177
+ alert_type: alert_type,
178
+ severity: severity,
179
+ title: title,
180
+ description: description,
181
+ location: location,
182
+ coordinates: options[:coordinates] || {},
183
+ affected_area_radius: options[:radius] || 0,
184
+ estimated_affected_people: options[:affected_people] || 0,
185
+ contact_info: options[:contact] || {},
186
+ metadata: options[:metadata] || {}
187
+ }.merge(options))
188
+
189
+ # Broadcast to all emergency services
190
+ alert._sm_header.from = 'dispatch_center'
191
+ alert._sm_header.to = 'emergency_broadcast'
192
+ alert.publish
193
+
194
+ # Route to specific services based on alert type
195
+ route_alert_to_services(alert, alert_type, severity)
196
+
197
+ @active_alerts[alert_id] = alert
198
+ alert_id
199
+ end
200
+
201
+ private
202
+
203
+ def handle_citizen_report(report_data)
204
+ puts "📞 Dispatch: Received citizen report #{report_data['report_id']}"
205
+
206
+ # Evaluate if report requires emergency alert
207
+ if report_data['urgency'] == 'high'
208
+ # Escalate to emergency alert
209
+ alert_id = create_alert(
210
+ determine_alert_type(report_data['description']),
211
+ 'medium',
212
+ "Citizen Report: #{report_data['report_type'].capitalize}",
213
+ report_data['description'],
214
+ report_data['location'],
215
+ { contact: { phone: report_data['contact_phone'] } }
216
+ )
217
+
218
+ puts " ⬆️ Escalated to emergency alert: #{alert_id}"
219
+ end
220
+ end
221
+
222
+ def track_response(response_data)
223
+ puts "📊 Dispatch: Tracking response #{response_data['response_id']} for alert #{response_data['alert_id']}"
224
+ @response_units[response_data['response_id']] = response_data
225
+ end
226
+
227
+ def route_alert_to_services(alert, alert_type, severity)
228
+ services = case alert_type
229
+ when 'fire'
230
+ ['fire_department', 'police_department'] + (severity == 'critical' ? ['hospital'] : [])
231
+ when 'medical'
232
+ ['hospital', 'fire_department']
233
+ when 'security'
234
+ ['police_department'] + (severity == 'critical' ? ['fire_department'] : [])
235
+ when 'natural_disaster'
236
+ ['fire_department', 'police_department', 'hospital', 'utility_company']
237
+ when 'infrastructure'
238
+ ['utility_company', 'fire_department']
239
+ else
240
+ ['police_department']
241
+ end
242
+
243
+ services.each do |service|
244
+ alert_copy = EmergencyAlert.new(alert.to_h)
245
+ alert_copy._sm_header.to = service
246
+ alert_copy.publish
247
+ end
248
+ end
249
+
250
+ def determine_alert_type(description)
251
+ case description.downcase
252
+ when /fire|smoke|burning/ then 'fire'
253
+ when /medical|injury|accident|hurt/ then 'medical'
254
+ when /robbery|theft|assault|suspicious/ then 'security'
255
+ when /flood|earthquake|storm|tornado/ then 'natural_disaster'
256
+ when /power|gas|water|sewer/ then 'infrastructure'
257
+ else 'security'
258
+ end
259
+ end
260
+ end
261
+
262
+ class FireDepartment
263
+ def initialize(transport)
264
+ @transport = transport
265
+ @available_units = 3
266
+ @deployed_units = {}
267
+ setup_subscriptions
268
+ end
269
+
270
+ def setup_subscriptions
271
+ # Listen for emergency alerts
272
+ @transport.where
273
+ .to('fire_department')
274
+ .subscribe do |message_class, message_data|
275
+ handle_alert(JSON.parse(message_data)) if message_class == 'EmergencyAlert'
276
+ end
277
+
278
+ # Listen for broadcasts
279
+ @transport.where
280
+ .to('emergency_broadcast')
281
+ .subscribe do |message_class, message_data|
282
+ monitor_alert(JSON.parse(message_data)) if message_class == 'EmergencyAlert'
283
+ end
284
+ end
285
+
286
+ private
287
+
288
+ def handle_alert(alert_data)
289
+ alert_type = alert_data['alert_type']
290
+ severity = alert_data['severity']
291
+ alert_id = alert_data['alert_id']
292
+
293
+ puts "🚒 Fire Department: Received #{severity} #{alert_type} alert #{alert_id}"
294
+
295
+ if should_respond?(alert_type, severity)
296
+ dispatch_response(alert_data)
297
+ else
298
+ puts " ⏸️ Standing by (not primary responder for this alert type)"
299
+ end
300
+ end
301
+
302
+ def monitor_alert(alert_data)
303
+ puts "🚒 Fire Department: Monitoring alert #{alert_data['alert_id']} (broadcast)"
304
+ end
305
+
306
+ def should_respond?(alert_type, severity)
307
+ case alert_type
308
+ when 'fire' then true
309
+ when 'medical' then true
310
+ when 'natural_disaster' then true
311
+ when 'infrastructure' then severity == 'high' || severity == 'critical'
312
+ when 'security' then severity == 'critical'
313
+ else false
314
+ end
315
+ end
316
+
317
+ def dispatch_response(alert_data)
318
+ if @available_units > 0
319
+ @available_units -= 1
320
+ response_id = "FD-#{Time.now.strftime('%H%M%S')}-#{rand(99)}"
321
+
322
+ personnel = case alert_data['severity']
323
+ when 'critical' then 8
324
+ when 'high' then 6
325
+ when 'medium' then 4
326
+ else 2
327
+ end
328
+
329
+ equipment = case alert_data['alert_type']
330
+ when 'fire' then ['fire_engine', 'ladder_truck', 'water_tank']
331
+ when 'medical' then ['ambulance', 'rescue_equipment']
332
+ else ['fire_engine', 'rescue_equipment']
333
+ end
334
+
335
+ EmergencyResponse.new(
336
+ response_id: response_id,
337
+ alert_id: alert_data['alert_id'],
338
+ responding_unit: 'fire_dept',
339
+ response_type: 'dispatched',
340
+ eta_minutes: rand(5..15),
341
+ personnel_count: personnel,
342
+ equipment: equipment,
343
+ status_update: "Unit dispatched to #{alert_data['location']}",
344
+ _sm_header: {
345
+ from: 'fire_department',
346
+ to: 'dispatch_center'
347
+ }
348
+ ).publish
349
+
350
+ @deployed_units[response_id] = alert_data['alert_id']
351
+
352
+ # Simulate arrival and resolution
353
+ Thread.new do
354
+ sleep(2) # Simulate travel time
355
+ update_response_status(response_id, 'on_scene', 'Fire department on scene, assessing situation')
356
+
357
+ sleep(3) # Simulate response time
358
+ resolution = ['resolved', 'resolved', 'resolved', 'cancelled'].sample # 75% success rate
359
+ status = resolution == 'resolved' ? 'Incident resolved successfully' : 'False alarm - no action required'
360
+ update_response_status(response_id, resolution, status)
361
+
362
+ @available_units += 1
363
+ @deployed_units.delete(response_id)
364
+ end
365
+ else
366
+ puts " 🚫 No available units - requesting mutual aid"
367
+ end
368
+ end
369
+
370
+ def update_response_status(response_id, response_type, status)
371
+ EmergencyResponse.new(
372
+ response_id: response_id,
373
+ alert_id: @deployed_units[response_id],
374
+ responding_unit: 'fire_dept',
375
+ response_type: response_type,
376
+ status_update: status,
377
+ _sm_header: {
378
+ from: 'fire_department',
379
+ to: 'dispatch_center'
380
+ }
381
+ ).publish
382
+ end
383
+ end
384
+
385
+ class PoliceDepartment
386
+ def initialize(transport)
387
+ @transport = transport
388
+ @available_units = 5
389
+ @deployed_units = {}
390
+ setup_subscriptions
391
+ end
392
+
393
+ def setup_subscriptions
394
+ @transport.where
395
+ .to('police_department')
396
+ .subscribe do |message_class, message_data|
397
+ handle_alert(JSON.parse(message_data)) if message_class == 'EmergencyAlert'
398
+ end
399
+
400
+ @transport.where
401
+ .to('emergency_broadcast')
402
+ .subscribe do |message_class, message_data|
403
+ monitor_alert(JSON.parse(message_data)) if message_class == 'EmergencyAlert'
404
+ end
405
+ end
406
+
407
+ private
408
+
409
+ def handle_alert(alert_data)
410
+ alert_type = alert_data['alert_type']
411
+ severity = alert_data['severity']
412
+ alert_id = alert_data['alert_id']
413
+
414
+ puts "👮 Police Department: Received #{severity} #{alert_type} alert #{alert_id}"
415
+
416
+ if should_respond?(alert_type, severity)
417
+ dispatch_response(alert_data)
418
+ end
419
+ end
420
+
421
+ def monitor_alert(alert_data)
422
+ puts "👮 Police Department: Monitoring alert #{alert_data['alert_id']}"
423
+ end
424
+
425
+ def should_respond?(alert_type, severity)
426
+ case alert_type
427
+ when 'security' then true
428
+ when 'fire' then severity == 'medium' || severity == 'high' || severity == 'critical'
429
+ when 'natural_disaster' then true
430
+ when 'medical' then severity == 'high' || severity == 'critical'
431
+ else false
432
+ end
433
+ end
434
+
435
+ def dispatch_response(alert_data)
436
+ if @available_units > 0
437
+ @available_units -= 1
438
+ response_id = "PD-#{Time.now.strftime('%H%M%S')}-#{rand(99)}"
439
+
440
+ officers = case alert_data['severity']
441
+ when 'critical' then 6
442
+ when 'high' then 4
443
+ when 'medium' then 2
444
+ else 1
445
+ end
446
+
447
+ equipment = case alert_data['alert_type']
448
+ when 'security' then ['patrol_car', 'radio', 'backup_requested']
449
+ else ['patrol_car', 'radio']
450
+ end
451
+
452
+ EmergencyResponse.new(
453
+ response_id: response_id,
454
+ alert_id: alert_data['alert_id'],
455
+ responding_unit: 'police',
456
+ response_type: 'dispatched',
457
+ eta_minutes: rand(3..12),
458
+ personnel_count: officers,
459
+ equipment: equipment,
460
+ status_update: "Officers dispatched to #{alert_data['location']}",
461
+ _sm_header: {
462
+ from: 'police_department',
463
+ to: 'dispatch_center'
464
+ }
465
+ ).publish
466
+
467
+ @deployed_units[response_id] = alert_data['alert_id']
468
+
469
+ Thread.new do
470
+ sleep(1.5)
471
+ update_response_status(response_id, 'on_scene', 'Police on scene, securing area')
472
+
473
+ sleep(2.5)
474
+ update_response_status(response_id, 'resolved', 'Situation resolved, area secured')
475
+
476
+ @available_units += 1
477
+ @deployed_units.delete(response_id)
478
+ end
479
+ end
480
+ end
481
+
482
+ def update_response_status(response_id, response_type, status)
483
+ EmergencyResponse.new(
484
+ response_id: response_id,
485
+ alert_id: @deployed_units[response_id],
486
+ responding_unit: 'police',
487
+ response_type: response_type,
488
+ status_update: status,
489
+ _sm_header: {
490
+ from: 'police_department',
491
+ to: 'dispatch_center'
492
+ }
493
+ ).publish
494
+ end
495
+ end
496
+
497
+ class Hospital
498
+ def initialize(transport)
499
+ @transport = transport
500
+ @available_ambulances = 2
501
+ @deployed_ambulances = {}
502
+ setup_subscriptions
503
+ end
504
+
505
+ def setup_subscriptions
506
+ @transport.where
507
+ .to('hospital')
508
+ .subscribe do |message_class, message_data|
509
+ handle_alert(JSON.parse(message_data)) if message_class == 'EmergencyAlert'
510
+ end
511
+ end
512
+
513
+ private
514
+
515
+ def handle_alert(alert_data)
516
+ alert_type = alert_data['alert_type']
517
+ severity = alert_data['severity']
518
+
519
+ puts "🏥 Hospital: Received #{severity} #{alert_type} alert #{alert_data['alert_id']}"
520
+
521
+ if alert_type == 'medical' || (alert_type == 'fire' && severity == 'high')
522
+ dispatch_ambulance(alert_data)
523
+ else
524
+ puts " 📋 Preparing to receive potential patients"
525
+ end
526
+ end
527
+
528
+ def dispatch_ambulance(alert_data)
529
+ if @available_ambulances > 0
530
+ @available_ambulances -= 1
531
+ response_id = "AMB-#{Time.now.strftime('%H%M%S')}-#{rand(99)}"
532
+
533
+ EmergencyResponse.new(
534
+ response_id: response_id,
535
+ alert_id: alert_data['alert_id'],
536
+ responding_unit: 'hospital',
537
+ response_type: 'dispatched',
538
+ eta_minutes: rand(6..18),
539
+ personnel_count: 2,
540
+ equipment: ['ambulance', 'medical_equipment', 'defibrillator'],
541
+ status_update: "Ambulance dispatched to #{alert_data['location']}",
542
+ _sm_header: {
543
+ from: 'hospital',
544
+ to: 'dispatch_center'
545
+ }
546
+ ).publish
547
+
548
+ @deployed_ambulances[response_id] = alert_data['alert_id']
549
+ end
550
+ end
551
+ end
552
+
553
+ #==============================================================================
554
+ # Emergency Alert System Demo
555
+ #==============================================================================
556
+
557
+ puts "\n🚨 Initializing Emergency Response System..."
558
+
559
+ transport = SmartMessage::Transport::RedisQueueTransport.new(
560
+ url: 'redis://localhost:6379',
561
+ db: 6,
562
+ queue_prefix: 'emergency_alerts',
563
+ consumer_group: 'emergency_responders',
564
+ block_time: 1000
565
+ )
566
+
567
+ # Initialize emergency services
568
+ dispatch = EmergencyDispatchCenter.new(transport)
569
+ fire_dept = FireDepartment.new(transport)
570
+ police = PoliceDepartment.new(transport)
571
+ hospital = Hospital.new(transport)
572
+
573
+ sleep 2
574
+ puts "✅ All emergency services online and ready"
575
+
576
+ #==============================================================================
577
+ # Emergency Scenarios
578
+ #==============================================================================
579
+
580
+ puts "\n🎭 Emergency Alert Scenarios:"
581
+
582
+ # Scenario 1: Building Fire
583
+ puts "\n🔥 Scenario 1: Building Fire Emergency"
584
+ fire_alert_id = dispatch.create_alert(
585
+ 'fire',
586
+ 'high',
587
+ 'Building Fire at Downtown Office Complex',
588
+ 'Large office building on fire, multiple floors affected, people may be trapped',
589
+ '123 Main Street, Downtown',
590
+ {
591
+ coordinates: { lat: 40.7128, lng: -74.0060 },
592
+ affected_people: 200,
593
+ radius: 2,
594
+ contact: { phone: '911', name: 'Dispatch Center' }
595
+ }
596
+ )
597
+
598
+ sleep 4
599
+
600
+ # Scenario 2: Medical Emergency
601
+ puts "\n🚑 Scenario 2: Medical Emergency"
602
+ medical_alert_id = dispatch.create_alert(
603
+ 'medical',
604
+ 'critical',
605
+ 'Multi-Vehicle Accident with Injuries',
606
+ 'Three-car collision on highway, multiple injuries, road blocked',
607
+ 'Highway 101 Mile Marker 45',
608
+ {
609
+ coordinates: { lat: 40.7589, lng: -73.9851 },
610
+ affected_people: 8,
611
+ radius: 1,
612
+ contact: { phone: '911', name: 'Highway Patrol' }
613
+ }
614
+ )
615
+
616
+ sleep 4
617
+
618
+ # Scenario 3: Security Incident
619
+ puts "\n👮 Scenario 3: Security Emergency"
620
+ security_alert_id = dispatch.create_alert(
621
+ 'security',
622
+ 'medium',
623
+ 'Armed Robbery in Progress',
624
+ 'Armed suspect robbing convenience store, suspect described as male, 5\'10", wearing black hoodie',
625
+ '456 Oak Avenue, West Side',
626
+ {
627
+ coordinates: { lat: 40.7505, lng: -73.9934 },
628
+ affected_people: 5,
629
+ contact: { phone: '911', name: 'Store Manager' }
630
+ }
631
+ )
632
+
633
+ sleep 4
634
+
635
+ # Scenario 4: Natural Disaster
636
+ puts "\n🌪️ Scenario 4: Natural Disaster"
637
+ disaster_alert_id = dispatch.create_alert(
638
+ 'natural_disaster',
639
+ 'critical',
640
+ 'Tornado Warning - Immediate Shelter Required',
641
+ 'Large tornado spotted 5 miles south, moving northeast at 45 mph, take shelter immediately',
642
+ 'Central County Area',
643
+ {
644
+ coordinates: { lat: 40.7282, lng: -74.0776 },
645
+ affected_people: 50000,
646
+ radius: 15,
647
+ contact: { phone: 'Emergency Management', name: 'County Emergency Services' }
648
+ }
649
+ )
650
+
651
+ sleep 5
652
+
653
+ #==============================================================================
654
+ # Citizen Reports
655
+ #==============================================================================
656
+
657
+ puts "\n📱 Citizen Reports:"
658
+
659
+ # Citizen Report 1: Suspicious Activity
660
+ CitizenReport.new(
661
+ report_id: "CR-#{Time.now.strftime('%Y%m%d%H%M%S')}",
662
+ reporter_id: 'citizen_123',
663
+ report_type: 'tip',
664
+ description: 'Suspicious person looking into car windows in parking lot',
665
+ location: '789 Pine Street parking lot',
666
+ urgency: 'medium',
667
+ contact_phone: '+1-555-0123',
668
+ _sm_header: {
669
+ from: 'citizen_app',
670
+ to: 'dispatch_center'
671
+ }
672
+ ).publish
673
+
674
+ sleep 2
675
+
676
+ # Citizen Report 2: Gas Leak
677
+ CitizenReport.new(
678
+ report_id: "CR-#{Time.now.strftime('%Y%m%d%H%M%S')}",
679
+ reporter_id: 'citizen_456',
680
+ report_type: 'incident',
681
+ description: 'Strong gas smell near apartment building, possible gas leak',
682
+ location: '321 Elm Street Apartments',
683
+ urgency: 'high',
684
+ contact_phone: '+1-555-0456',
685
+ photos: ['gas_leak_1.jpg', 'gas_leak_2.jpg'],
686
+ _sm_header: {
687
+ from: 'citizen_app',
688
+ to: 'dispatch_center'
689
+ }
690
+ ).publish
691
+
692
+ sleep 3
693
+
694
+ #==============================================================================
695
+ # Mass Alert Scenario
696
+ #==============================================================================
697
+
698
+ puts "\n📢 Mass Alert Scenario: Multiple Simultaneous Emergencies"
699
+
700
+ # Simulate multiple emergencies happening at once
701
+ emergencies = [
702
+ ['fire', 'medium', 'Kitchen Fire at Restaurant', 'Grease fire in commercial kitchen'],
703
+ ['medical', 'high', 'Heart Attack at Gym', 'Elderly man collapsed during workout'],
704
+ ['security', 'low', 'Vandalism Report', 'Graffiti on public building'],
705
+ ['infrastructure', 'medium', 'Water Main Break', 'Large water main break flooding street']
706
+ ]
707
+
708
+ puts "⚡ Simultaneous emergency alerts..."
709
+ emergencies.each_with_index do |(type, severity, title, description), i|
710
+ dispatch.create_alert(
711
+ type,
712
+ severity,
713
+ title,
714
+ description,
715
+ "Location #{i + 1}",
716
+ { affected_people: rand(10..100) }
717
+ )
718
+
719
+ sleep 0.5 # Slight delay between alerts
720
+ end
721
+
722
+ sleep 8
723
+
724
+ #==============================================================================
725
+ # System Statistics
726
+ #==============================================================================
727
+
728
+ puts "\n📊 Emergency Alert System Statistics:"
729
+
730
+ stats = transport.queue_stats
731
+ puts "\nQueue statistics:"
732
+ emergency_queues = stats.select { |name, _| name.include?('emergency') || name.include?('department') || name.include?('hospital') }
733
+ emergency_queues.each do |queue_name, info|
734
+ service_name = queue_name.split('.').last.tr('_', ' ').titleize
735
+ puts " #{service_name}: #{info[:length]} pending alerts"
736
+ end
737
+
738
+ routing_table = transport.routing_table
739
+ puts "\nActive routing patterns:"
740
+ routing_table.each do |pattern, queues|
741
+ puts " '#{pattern}' → #{queues.size} queue(s)"
742
+ end
743
+
744
+ total_alerts = stats.values.sum { |info| info[:length] }
745
+ puts "\nTotal active alerts in system: #{total_alerts}"
746
+
747
+ # Show alert type distribution
748
+ alert_types = ['fire', 'medical', 'security', 'natural_disaster', 'infrastructure']
749
+ puts "\nAlert Type Coverage:"
750
+ alert_types.each do |type|
751
+ pattern_exists = routing_table.any? { |pattern, _| pattern.downcase.include?(type) }
752
+ puts " #{type.capitalize}: #{pattern_exists ? '✅ Covered' : '❌ No coverage'}"
753
+ end
754
+
755
+ transport.disconnect
756
+
757
+ puts "\n🚨 Emergency Alert System demonstration completed!"
758
+
759
+ puts "\n💡 Emergency System Features Demonstrated:"
760
+ puts " ✓ Multi-service emergency coordination"
761
+ puts " ✓ Severity-based alert routing"
762
+ puts " ✓ Real-time response tracking"
763
+ puts " ✓ Citizen reporting integration"
764
+ puts " ✓ Broadcast and targeted alerts"
765
+ puts " ✓ Multi-agency response coordination"
766
+ puts " ✓ Geographic area coverage"
767
+ puts " ✓ Mass casualty incident handling"
768
+
769
+ puts "\n🚀 Key Emergency Response Benefits:"
770
+ puts " • Instant alert distribution to all relevant agencies"
771
+ puts " • Coordinated multi-agency response"
772
+ puts " • Real-time status updates and tracking"
773
+ puts " • Citizen engagement and reporting"
774
+ puts " • Scalable from single incident to mass casualty"
775
+ puts " • Persistent alert queues ensure no missed notifications"
776
+ puts " • Geographic and severity-based smart routing"
777
+ puts " • Historical incident tracking and analysis"