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,43 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/messages/emergency_resolved_message.rb
3
+ #
4
+ # Emergency resolution message broadcast when incidents are successfully resolved
5
+ # Provides closure notification to all city services about completed emergency responses
6
+ #
7
+ require_relative '../../../lib/smart_message'
8
+
9
+
10
+ module Messages
11
+ class EmergencyResolvedMessage < SmartMessage::Base
12
+ version 1
13
+
14
+ description 'Emergency incident closure notification message broadcast by emergency services when incidents are successfully resolved, providing completion status, response duration, outcome details, and unit deployment information to all city services for operational awareness and incident tracking'
15
+
16
+ transport SmartMessage::Transport::RedisTransport.new
17
+ serializer SmartMessage::Serializer::Json.new
18
+
19
+ property :incident_id, required: true,
20
+ description: 'Unique identifier of the emergency incident that was resolved'
21
+
22
+ property :incident_type, required: true,
23
+ description: "Classification of the incident that was resolved (e.g., 'robbery', 'kitchen fire')"
24
+
25
+ property :location, required: true,
26
+ description: 'Street address or location where the incident occurred and was resolved'
27
+
28
+ property :resolved_by, required: true,
29
+ description: "Name of the city service that resolved the incident (e.g., 'police-department', 'fire-department')"
30
+
31
+ property :resolution_time, required: true,
32
+ description: 'Exact time when the incident was fully resolved (YYYY-MM-DD HH:MM:SS format)'
33
+
34
+ property :duration_minutes, required: true,
35
+ description: 'Total duration of the incident from start to resolution in minutes (decimal format)'
36
+
37
+ property :outcome,
38
+ description: 'Brief description of the final result or outcome of the emergency response'
39
+
40
+ property :units_involved,
41
+ description: 'Array of emergency response units that participated in resolving the incident'
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/messages/fire_dispatch_message.rb
3
+ #
4
+ # Fire dispatch message sent by the Fire Department in response to fire emergencies
5
+ # Contains fire engine assignments and specialized equipment details for fire suppression operations
6
+
7
+ require_relative '../../../lib/smart_message'
8
+
9
+ module Messages
10
+ class FireDispatchMessage < SmartMessage::Base
11
+ version 1
12
+
13
+ description 'Emergency fire suppression dispatch coordination message sent by the Fire Department in response to fire emergencies, containing fire engine assignments, specialized equipment deployment, and tactical response information for various fire types, rescue operations, and hazardous material incidents'
14
+
15
+ transport SmartMessage::Transport::RedisTransport.new
16
+ serializer SmartMessage::Serializer::Json.new
17
+
18
+ property :dispatch_id, required: true,
19
+ description: 'Unique hexadecimal identifier for this fire department dispatch operation'
20
+
21
+ property :engines_assigned, required: true,
22
+ description: "Array of fire engine and apparatus call signs responding (e.g., ['Engine-1', 'Ladder-1', 'Rescue-1'])"
23
+
24
+ VALID_FIRE_TYPES = %w[fire kitchen electrical basement garage wildfire]
25
+
26
+ property :location, required: true,
27
+ description: 'Street address or location where fire engines should respond to the emergency'
28
+
29
+ property :fire_type, required: true,
30
+ validate: ->(v) { VALID_FIRE_TYPES.include?(v) },
31
+ validation_message: "Fire type must be: #{VALID_FIRE_TYPES.join(', ')}",
32
+ description: "Classification of the fire type requiring specific suppression methods. Valid values: #{VALID_FIRE_TYPES.join(', ')}"
33
+
34
+ property :equipment_needed,
35
+ description: 'Specialized firefighting equipment and tools required for this specific fire type'
36
+
37
+ property :estimated_arrival,
38
+ description: "Projected time for first fire engine arrival at the emergency scene (e.g., '6 minutes')"
39
+
40
+ property :timestamp, required: true,
41
+ description: 'Exact time when the fire dispatch was initiated (YYYY-MM-DD HH:MM:SS format)'
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/messages/fire_emergency_message.rb
3
+ #
4
+ # Fire emergency message sent by houses to the Fire Department when fires are detected
5
+ # Triggers immediate fire department response with engine dispatch based on fire severity and type
6
+ #
7
+ require_relative '../../../lib/smart_message'
8
+
9
+ module Messages
10
+ class FireEmergencyMessage < SmartMessage::Base
11
+ version 1
12
+
13
+ description "Critical fire emergency alert message sent by residential monitoring systems to the Fire Department when smoke, heat, or fire is detected, triggering immediate emergency response with fire engine dispatch, specialized equipment deployment, and rescue operations based on fire type, severity, and occupant safety status"
14
+
15
+ transport SmartMessage::Transport::RedisTransport.new
16
+ serializer SmartMessage::Serializer::Json.new
17
+
18
+ VALID_FIRE_TYPES = %w[fire kitchen electrical basement garage wildfire]
19
+ VALID_SEVERITY = %w[small medium large out_of_control]
20
+ VALID_OCCUPANTS_STATUS = %w[safe evacuated trapped rescued]
21
+ VALID_SPREAD_RISK = %w[low medium high]
22
+
23
+ property :house_address, required: true,
24
+ description: "Full street address of the house where fire was detected"
25
+
26
+ property :fire_type, required: true,
27
+ validate: ->(v) { VALID_FIRE_TYPES.include?(v) },
28
+ validation_message: "Fire type must be: #{VALID_FIRE_TYPES.join(', ')}",
29
+ description: "Classification of fire origin and type. Valid values: #{VALID_FIRE_TYPES.join(', ')}"
30
+
31
+ property :severity, required: true,
32
+ validate: ->(v) { VALID_SEVERITY.include?(v) },
33
+ validation_message: "Severity must be: #{VALID_SEVERITY.join(', ')}",
34
+ description: "Current intensity and spread level of the fire. Valid values: #{VALID_SEVERITY.join(', ')}"
35
+
36
+ property :timestamp, required: true,
37
+ description: "Exact time when the fire was first detected (YYYY-MM-DD HH:MM:SS format)"
38
+
39
+ property :occupants_status,
40
+ description: "Current safety status of people in the house. Valid values: #{VALID_OCCUPANTS_STATUS.join(', ')}"
41
+
42
+ property :spread_risk,
43
+ description: "Assessment of fire's potential to spread to neighboring structures. Valid values: #{VALID_SPREAD_RISK.join(', ')}"
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ # messages/health_check_message.rb
3
+ #
4
+ # Health check message broadcast by the Health Department to monitor city services
5
+ # Sent every 5 seconds to all services to verify operational status
6
+
7
+ require_relative '../../../lib/smart_message'
8
+
9
+ module Messages
10
+
11
+ class HealthCheckMessage < SmartMessage::Base
12
+ version 1
13
+
14
+ description "Health monitoring message broadcast by the Health Department every 5 seconds to verify operational status of all city services including police, fire, banks, and residential monitoring systems"
15
+
16
+ transport SmartMessage::Transport::RedisTransport.new
17
+ serializer SmartMessage::Serializer::Json.new
18
+
19
+ property :check_id, required: true,
20
+ description: "Unique identifier for this health check request (UUID format)"
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # messages/health_status_message.rb
3
+ #
4
+ # Health status response message sent by city services back to the Health Department
5
+ # Contains operational status with color-coded display (green=healthy, yellow=warning, orange=critical, red=failed)
6
+
7
+ require_relative '../../../lib/smart_message'
8
+
9
+ module Messages
10
+
11
+ class HealthStatusMessage < SmartMessage::Base
12
+ version 1
13
+
14
+ description "Operational status response message sent by city services to the Health Department in response to health check requests, providing real-time service condition reporting with color-coded status levels for monitoring dashboard display"
15
+
16
+ VALID_STATUS = %w[healthy warning critical failed]
17
+
18
+ transport SmartMessage::Transport::RedisTransport.new
19
+ serializer SmartMessage::Serializer::Json.new
20
+
21
+ property :service_name, required: true,
22
+ description: "Name of the city service reporting its status (e.g., 'police-department', 'fire-department')"
23
+
24
+ property :check_id, required: true,
25
+ description: "ID of the health check request this message responds to"
26
+
27
+ property :status, required: true,
28
+ validate: ->(v) { VALID_STATUS.include?(v) },
29
+ validation_message: "Status must be: #{VALID_STATUS.join(', ')}",
30
+ description: "Current operational status of the service. Valid values: #{VALID_STATUS.join(', ')}"
31
+
32
+ property :details,
33
+ description: "Additional status information describing current service conditions"
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ # messages/police_dispatch_message.rb
3
+ # Police dispatch message sent by the Police Department in response to emergency calls
4
+ # Contains unit assignments and response details for security incidents and crimes
5
+
6
+ require_relative '../../../lib/smart_message'
7
+
8
+ module Messages
9
+ class PoliceDispatchMessage < SmartMessage::Base
10
+ version 1
11
+
12
+ description <<~DESC
13
+ Law enforcement dispatch coordination message sent by the Police Department in response to emergency calls
14
+ and security incidents, containing unit assignments, response priorities, and tactical deployment information
15
+ for crimes, alarms, and public safety threats
16
+ DESC
17
+
18
+ transport SmartMessage::Transport::RedisTransport.new
19
+ serializer SmartMessage::Serializer::Json.new
20
+
21
+ VALID_PRIORITY = %w[low medium high emergency]
22
+
23
+ property :dispatch_id, required: true,
24
+ description: "Unique hexadecimal identifier for this police dispatch operation"
25
+
26
+ property :units_assigned, required: true,
27
+ description: "Array of police unit call signs assigned to respond (e.g., ['Unit-101', 'Unit-102'])"
28
+
29
+ property :location, required: true,
30
+ description: "Street address or location where police units should respond"
31
+
32
+ property :incident_type, required: true,
33
+ description: "Classification of the incident requiring police response (e.g., 'robbery', 'suspicious_activity')"
34
+
35
+ property :priority, required: true,
36
+ validate: ->(v) { VALID_PRIORITY.include?(v) },
37
+ validation_message: "Priority must be: #{VALID_PRIORITY.join(', ')}",
38
+ description: "Response urgency level determining dispatch speed. Valid values: #{VALID_PRIORITY.join(', ')}"
39
+
40
+ property :estimated_arrival,
41
+ description: "Projected time for first unit arrival at the scene (e.g., '5 minutes')"
42
+
43
+ property :timestamp, required: true,
44
+ description: "Exact time when the dispatch was initiated (YYYY-MM-DD HH:MM:SS format)"
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # messages/silent_alarm_message.rb
3
+ # Silent alarm message sent by banks to the Police Department during security incidents
4
+ # Triggers immediate police response with unit dispatch based on severity level
5
+
6
+ require_relative '../../../lib/smart_message'
7
+
8
+ module Messages
9
+ class SilentAlarmMessage < SmartMessage::Base
10
+ version 1
11
+
12
+ description <<~DESC
13
+ An automatic emergency security alert message sent by banking institutions, stores or houses
14
+ with monitored secutity systems. This message is used to the Police Department when security breaches,
15
+ robberies, or suspicious activities are detected, triggering immediate law enforcement response with
16
+ automatic unit dispatch based on the threat severity level.
17
+ DESC
18
+
19
+ transport SmartMessage::Transport::RedisTransport.new
20
+ serializer SmartMessage::Serializer::Json.new
21
+
22
+ VALID_ALARM_TYPES = %w[robbery vault_breach suspicious_activity]
23
+ VALID_SEVERITY = %w[low medium high critical]
24
+
25
+ property :bank_name, required: true, description: "Official name of the bank triggering the alarm (e.g., 'First National Bank')"
26
+ property :location, required: true, description: "Physical address of the bank location where alarm was triggered"
27
+ property :alarm_type, required: true,
28
+ validate: ->(v) { VALID_ALARM_TYPES.include?(v) },
29
+ validation_message: "Alarm type must be: #{VALID_ALARM_TYPES.join(', ')}",
30
+ description: "Type of security incident detected. Valid values: #{VALID_ALARM_TYPES.join(', ')}"
31
+ property :timestamp, required: true, description: "Exact time when the alarm was triggered (YYYY-MM-DD HH:MM:SS format)"
32
+ property :severity, required: true,
33
+ validate: ->(v) { VALID_SEVERITY.include?(v) },
34
+ validation_message: "Severity must be: #{VALID_SEVERITY.join(', ')}",
35
+ description: "Urgency level of the security threat. Valid values: #{VALID_SEVERITY.join(', ')}"
36
+ property :details, description: "Additional descriptive information about the security incident and current situation"
37
+ end
38
+ end
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/multi_program_demo/police_department.rb
3
+
4
+ require_relative '../../lib/smart_message'
5
+ require_relative 'messages/health_check_message'
6
+ require_relative 'messages/health_status_message'
7
+ require_relative 'messages/silent_alarm_message'
8
+ require_relative 'messages/police_dispatch_message'
9
+ require_relative 'messages/emergency_resolved_message'
10
+ require_relative 'messages/emergency_911_message'
11
+
12
+ require_relative 'common/health_monitor'
13
+ require_relative 'common/logger'
14
+
15
+ class PoliceDepartment
16
+ include Common::HealthMonitor
17
+ include Common::Logger
18
+
19
+ def initialize
20
+ @service_name = 'police_department'
21
+ @status = 'healthy'
22
+ @start_time = Time.now
23
+ @active_incidents = {}
24
+ @available_units = ['Unit-101', 'Unit-102', 'Unit-103', 'Unit-104']
25
+
26
+ setup_messaging
27
+ setup_signal_handlers
28
+ setup_health_monitor
29
+ end
30
+
31
+
32
+ def setup_messaging
33
+ Messages::EmergencyResolvedMessage.from(@service_name)
34
+
35
+ # Subscribe to silent alarms from banks
36
+ Messages::SilentAlarmMessage.subscribe(to: @service_name) do |message|
37
+ puts "🚔 DEBUG: Received SilentAlarmMessage to: #{message._sm_header.to}"
38
+ handle_silent_alarm(message)
39
+ end
40
+
41
+ # Subscribe to 911 calls routed from dispatch
42
+ Messages::Emergency911Message.subscribe(to: [@service_name, /police/i]) do |message|
43
+ puts "🚔 DEBUG: Received Emergency911Message to: #{message._sm_header.to}"
44
+ handle_911_call(message)
45
+ end
46
+
47
+ puts "🚔 Police Department operational"
48
+ puts " Available units: #{@available_units.join(', ')}"
49
+ puts " Responding to health checks, silent alarms, and 911 calls"
50
+ puts " Press Ctrl+C to stop\n\n"
51
+ logger.info("Police Department operational with units: #{@available_units.join(', ')}")
52
+ end
53
+
54
+ def setup_signal_handlers
55
+ %w[INT TERM].each do |signal|
56
+ Signal.trap(signal) do
57
+ puts "\n🚔 Police Department signing off..."
58
+ logger.info("Police Department signing off")
59
+ exit(0)
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ def start_service
66
+ loop do
67
+ check_incident_resolutions
68
+ sleep(2)
69
+ end
70
+ rescue => e
71
+ puts "🚔 Error in police service: #{e.message}"
72
+ logger.error("Error in police service: #{e.message}")
73
+ retry
74
+ end
75
+
76
+ private
77
+
78
+ def service_emoji
79
+ "🚔"
80
+ end
81
+
82
+ def get_status_details
83
+ # Determine status based on available units and active incidents
84
+ @status = if @available_units.empty?
85
+ 'critical'
86
+ elsif @available_units.size <= 1
87
+ 'warning'
88
+ elsif @active_incidents.size >= 3
89
+ 'warning'
90
+ else
91
+ 'healthy'
92
+ end
93
+
94
+ details = case @status
95
+ when 'healthy' then "All units operational, #{@active_incidents.size} active incidents"
96
+ when 'warning' then "High call volume, #{@active_incidents.size} active incidents"
97
+ when 'critical' then "Multiple emergencies, all units deployed"
98
+ when 'failed' then "System down, emergency protocols activated"
99
+ end
100
+
101
+ [@status, details]
102
+ end
103
+
104
+ def handle_silent_alarm(alarm)
105
+ # Process silent alarm message
106
+ puts "🚨 SILENT ALARM: #{alarm.bank_name} at #{alarm.location} - #{alarm.alarm_type.upcase} (#{alarm.severity} severity)"
107
+ puts " Details: #{alarm.details}" if alarm.details
108
+ puts " Time: #{alarm.timestamp}"
109
+ logger.warn("SILENT ALARM received: #{alarm.alarm_type} at #{alarm.location} (#{alarm.severity} severity)")
110
+
111
+ # Assign available units
112
+ units_needed = case alarm.severity
113
+ when 'low' then 1
114
+ when 'medium' then 2
115
+ when 'high' then 3
116
+ when 'critical' then 4
117
+ else 2
118
+ end
119
+
120
+ assigned_units = @available_units.take(units_needed)
121
+ @available_units = @available_units.drop(units_needed)
122
+
123
+ dispatch_id = SecureRandom.hex(4)
124
+ @active_incidents[dispatch_id] = {
125
+ location: alarm.location,
126
+ start_time: Time.now,
127
+ units: assigned_units,
128
+ type: alarm.alarm_type
129
+ }
130
+
131
+ # Send dispatch message
132
+ dispatch = Messages::PoliceDispatchMessage.new(
133
+ dispatch_id: dispatch_id,
134
+ units_assigned: assigned_units,
135
+ location: alarm.location,
136
+ incident_type: alarm.alarm_type,
137
+ priority: map_severity_to_priority(alarm.severity),
138
+ estimated_arrival: "#{rand(3..8)} minutes",
139
+ timestamp: Time.now.strftime('%Y-%m-%d %H:%M:%S'),
140
+ )
141
+ dispatch.publish
142
+
143
+ puts "🚔 Dispatched #{assigned_units.size} units to #{alarm.location}"
144
+ puts " Available units: #{@available_units.size}"
145
+ logger.info("Dispatched units #{assigned_units.join(', ')} to #{alarm.location} (#{dispatch_id})")
146
+ rescue => e
147
+ puts "🚔 Error handling silent alarm: #{e.message}"
148
+ logger.error("Error handling silent alarm: #{e.message}")
149
+ end
150
+
151
+ def handle_911_call(call)
152
+ # Color code based on emergency type
153
+ type_color = case call.emergency_type
154
+ when 'accident' then "\e[33m" # Yellow
155
+ when 'crime' then "\e[31m" # Red
156
+ else "\e[36m" # Cyan
157
+ end
158
+
159
+ puts "\n#{type_color}🚔 911 CALL RECEIVED\e[0m from dispatch"
160
+ puts " 📍 Location: #{call.caller_location}"
161
+ puts " 🚨 Type: #{call.emergency_type.upcase}"
162
+ puts " 📝 Description: #{call.description}"
163
+ puts " 👤 Caller: #{call.caller_name || 'Unknown'}"
164
+
165
+ logger.warn("911 Call: #{call.emergency_type} at #{call.caller_location} - #{call.description}")
166
+
167
+ # Handle based on emergency type
168
+ case call.emergency_type
169
+ when 'accident'
170
+ handle_accident_call(call)
171
+ when 'crime'
172
+ handle_crime_call(call)
173
+ else
174
+ handle_general_911_call(call)
175
+ end
176
+ rescue => e
177
+ puts "🚔 Error handling 911 call: #{e.message}"
178
+ logger.error("Error handling 911 call: #{e.message}")
179
+ end
180
+
181
+ def handle_accident_call(call)
182
+ incident_id = "ACC-#{Time.now.strftime('%H%M%S')}"
183
+
184
+ # Determine units needed based on severity
185
+ units_needed = case call.severity
186
+ when 'critical' then 3
187
+ when 'high' then 2
188
+ else 1
189
+ end
190
+
191
+ units_needed = [units_needed, @available_units.size].min
192
+ assigned_units = @available_units.shift(units_needed)
193
+
194
+ if assigned_units.empty?
195
+ puts "🚔 ⚠️ No units available for accident response!"
196
+ logger.error("No units available for accident at #{call.caller_location}")
197
+ return
198
+ end
199
+
200
+ @active_incidents[incident_id] = {
201
+ type: 'accident',
202
+ location: call.caller_location,
203
+ units: assigned_units,
204
+ start_time: Time.now,
205
+ call: call
206
+ }
207
+
208
+ puts "🚔 Dispatched #{assigned_units.join(', ')} to accident at #{call.caller_location}"
209
+ puts " Vehicles involved: #{call.vehicles_involved || 'Unknown'}"
210
+ puts " Injuries: #{call.injuries_reported ? 'Yes' : 'No'}"
211
+ logger.info("Dispatched #{assigned_units.join(', ')} to accident #{incident_id}")
212
+ end
213
+
214
+ def handle_crime_call(call)
215
+ incident_id = "CRM-#{Time.now.strftime('%H%M%S')}"
216
+
217
+ # Determine units based on severity and weapons
218
+ units_needed = call.weapons_involved ? 3 : 2
219
+ units_needed = [units_needed, @available_units.size].min
220
+ assigned_units = @available_units.shift(units_needed)
221
+
222
+ if assigned_units.empty?
223
+ puts "🚔 ⚠️ No units available for crime response!"
224
+ logger.error("No units available for crime at #{call.caller_location}")
225
+ return
226
+ end
227
+
228
+ @active_incidents[incident_id] = {
229
+ type: 'crime',
230
+ location: call.caller_location,
231
+ units: assigned_units,
232
+ start_time: Time.now,
233
+ call: call
234
+ }
235
+
236
+ puts "🚔 Dispatched #{assigned_units.join(', ')} to crime scene at #{call.caller_location}"
237
+ puts " Weapons involved: #{call.weapons_involved ? 'YES' : 'No'}"
238
+ puts " Suspects on scene: #{call.suspects_on_scene ? 'YES' : 'Unknown'}"
239
+ logger.info("Dispatched #{assigned_units.join(', ')} to crime #{incident_id}")
240
+ end
241
+
242
+ def handle_general_911_call(call)
243
+ incident_id = "GEN-#{Time.now.strftime('%H%M%S')}"
244
+
245
+ assigned_units = @available_units.shift(1)
246
+
247
+ if assigned_units.empty?
248
+ puts "🚔 ⚠️ No units available for response!"
249
+ logger.error("No units available for call at #{call.caller_location}")
250
+ return
251
+ end
252
+
253
+ @active_incidents[incident_id] = {
254
+ type: call.emergency_type,
255
+ location: call.caller_location,
256
+ units: assigned_units,
257
+ start_time: Time.now,
258
+ call: call
259
+ }
260
+
261
+ puts "🚔 Dispatched #{assigned_units.join(', ')} to #{call.emergency_type} at #{call.caller_location}"
262
+ logger.info("Dispatched #{assigned_units.join(', ')} to #{call.emergency_type} #{incident_id}")
263
+ end
264
+
265
+ def check_incident_resolutions
266
+ @active_incidents.each do |incident_id, incident|
267
+ # Simulate incident resolution after 10-15 seconds
268
+ duration = (Time.now - incident[:start_time]).to_i
269
+ if duration > rand(10..15)
270
+ resolve_incident(incident_id, incident, duration)
271
+ end
272
+ end
273
+ end
274
+
275
+ def resolve_incident(incident_id, incident, duration_seconds)
276
+ # Return units to available pool
277
+ @available_units.concat(incident[:units])
278
+ @active_incidents.delete(incident_id)
279
+
280
+ outcomes = ['Suspects apprehended', 'False alarm - all clear', 'Incident resolved peacefully', 'Suspects fled scene']
281
+
282
+ resolution = Messages::EmergencyResolvedMessage.new(
283
+ incident_id: incident_id,
284
+ incident_type: incident[:type],
285
+ location: incident[:location],
286
+ resolved_by: @service_name,
287
+ resolution_time: Time.now.strftime('%Y-%m-%d %H:%M:%S'),
288
+ duration_minutes: (duration_seconds / 60.0).round(1),
289
+ outcome: outcomes.sample,
290
+ units_involved: incident[:units],
291
+ )
292
+ resolution.publish
293
+
294
+ puts "🚔 Incident #{incident_id} resolved after #{(duration_seconds / 60.0).round(1)} minutes"
295
+ puts " Units #{incident[:units].join(', ')} now available"
296
+ logger.info("Incident #{incident_id} resolved: #{outcomes.last} after #{(duration_seconds / 60.0).round(1)} minutes")
297
+ rescue => e
298
+ puts "🚔 Error resolving incident: #{e.message}"
299
+ logger.error("Error resolving incident: #{e.message}")
300
+ end
301
+
302
+ def map_severity_to_priority(severity)
303
+ case severity
304
+ when 'low' then 'low'
305
+ when 'medium' then 'medium'
306
+ when 'high' then 'high'
307
+ when 'critical' then 'emergency'
308
+ else 'medium'
309
+ end
310
+ end
311
+ end
312
+
313
+ if __FILE__ == $0
314
+ police_dept = PoliceDepartment.new
315
+ police_dept.start_service
316
+ end