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.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +38 -0
- data/.gitignore +5 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +35 -4
- data/README.md +169 -71
- data/Rakefile +29 -4
- data/docs/assets/images/ddq_architecture.svg +130 -0
- data/docs/assets/images/dlq_architecture.svg +115 -0
- data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
- data/docs/assets/images/enhanced-fluent-api.svg +149 -0
- data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
- data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
- data/docs/assets/images/fluent-api-demo.svg +59 -0
- data/docs/assets/images/performance-comparison.svg +161 -0
- data/docs/assets/images/redis-basic-architecture.svg +53 -0
- data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
- data/docs/assets/images/redis-queue-architecture.svg +101 -0
- data/docs/assets/images/smart_message.jpg +0 -0
- data/docs/assets/images/smart_message_walking.jpg +0 -0
- data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
- data/docs/assets/images/transport-comparison-matrix.svg +171 -0
- data/docs/assets/javascripts/mathjax.js +17 -0
- data/docs/assets/stylesheets/extra.css +51 -0
- data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
- data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
- data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
- data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
- data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
- data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
- data/docs/{examples.md → getting-started/examples.md} +115 -89
- data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
- data/docs/guides/redis-queue-getting-started.md +697 -0
- data/docs/guides/redis-queue-patterns.md +889 -0
- data/docs/guides/redis-queue-production.md +1091 -0
- data/docs/index.md +64 -0
- data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
- data/docs/{logging.md → reference/logging.md} +1 -1
- data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
- data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
- data/docs/{serializers.md → reference/serializers.md} +3 -5
- data/docs/{transports.md → reference/transports.md} +133 -11
- data/docs/transports/memory-transport.md +374 -0
- data/docs/transports/redis-enhanced-transport.md +524 -0
- data/docs/transports/redis-queue-transport.md +1304 -0
- data/docs/transports/redis-transport-comparison.md +496 -0
- data/docs/transports/redis-transport.md +509 -0
- data/examples/README.md +98 -5
- data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
- data/examples/city_scenario/README.md +515 -0
- data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
- data/examples/city_scenario/citizen.rb +195 -0
- data/examples/city_scenario/city_diagram.svg +125 -0
- data/examples/city_scenario/common/health_monitor.rb +80 -0
- data/examples/city_scenario/common/logger.rb +30 -0
- data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
- data/examples/city_scenario/fire_department.rb +446 -0
- data/examples/city_scenario/fire_emergency_flow.svg +95 -0
- data/examples/city_scenario/health_department.rb +100 -0
- data/examples/city_scenario/health_monitoring_system.svg +130 -0
- data/examples/city_scenario/house.rb +244 -0
- data/examples/city_scenario/local_bank.rb +217 -0
- data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
- data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
- data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
- data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
- data/examples/city_scenario/messages/health_check_message.rb +22 -0
- data/examples/city_scenario/messages/health_status_message.rb +35 -0
- data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
- data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
- data/examples/city_scenario/police_department.rb +316 -0
- data/examples/city_scenario/redis_monitor.rb +129 -0
- data/examples/city_scenario/redis_stats.rb +743 -0
- data/examples/city_scenario/room_for_improvement.md +240 -0
- data/examples/city_scenario/security_emergency_flow.svg +95 -0
- data/examples/city_scenario/service_internal_architecture.svg +154 -0
- data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
- data/examples/city_scenario/start_demo.sh +236 -0
- data/examples/city_scenario/stop_demo.sh +106 -0
- data/examples/city_scenario/visitor.rb +631 -0
- data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
- data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
- data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
- data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
- data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
- data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
- data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
- data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
- data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
- data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
- data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
- data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
- data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
- data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
- data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
- data/examples/memory/README.md +163 -0
- data/examples/memory/memory_transport_architecture.svg +90 -0
- data/examples/memory/point_to_point_pattern.svg +94 -0
- data/examples/memory/publish_subscribe_pattern.svg +125 -0
- data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
- data/examples/redis/README.md +230 -0
- data/examples/redis/alert_system_flow.svg +127 -0
- data/examples/redis/dashboard_status_flow.svg +107 -0
- data/examples/redis/device_command_flow.svg +113 -0
- data/examples/redis/redis_transport_architecture.svg +115 -0
- data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
- data/examples/redis/smart_home_system_architecture.svg +133 -0
- data/examples/redis_enhanced/README.md +319 -0
- data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/01_basic_messaging.rb +221 -0
- data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
- data/examples/redis_queue/02_pattern_routing.rb +405 -0
- data/examples/redis_queue/03_fluent_api.rb +422 -0
- data/examples/redis_queue/04_load_balancing.rb +486 -0
- data/examples/redis_queue/05_microservices.rb +735 -0
- data/examples/redis_queue/06_emergency_alerts.rb +777 -0
- data/examples/redis_queue/07_queue_management.rb +587 -0
- data/examples/redis_queue/README.md +366 -0
- data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/redis_queue_architecture.svg +148 -0
- data/ideas/README.md +41 -0
- data/ideas/agents.md +1001 -0
- data/ideas/database_transport.md +980 -0
- data/ideas/improvement.md +359 -0
- data/ideas/meshage.md +1788 -0
- data/ideas/message_discovery.md +178 -0
- data/ideas/message_schema.md +1381 -0
- data/lib/smart_message/.idea/.gitignore +8 -0
- data/lib/smart_message/.idea/markdown.xml +6 -0
- data/lib/smart_message/.idea/misc.xml +4 -0
- data/lib/smart_message/.idea/modules.xml +8 -0
- data/lib/smart_message/.idea/smart_message.iml +16 -0
- data/lib/smart_message/.idea/vcs.xml +6 -0
- data/lib/smart_message/addressing.rb +15 -0
- data/lib/smart_message/base.rb +0 -2
- data/lib/smart_message/configuration.rb +1 -1
- data/lib/smart_message/logger.rb +15 -4
- data/lib/smart_message/plugins.rb +5 -2
- data/lib/smart_message/serializer.rb +14 -0
- data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
- data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +34 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +5 -52
- data/mkdocs.yml +184 -0
- data/p2p_plan.md +326 -0
- data/p2p_roadmap.md +287 -0
- data/smart_message.gemspec +2 -0
- data/smart_message.svg +51 -0
- metadata +170 -44
- data/docs/README.md +0 -57
- data/examples/dead_letters.jsonl +0 -12
- data/examples/temp.txt +0 -94
- data/examples/tmux_chat/README.md +0 -283
- data/examples/tmux_chat/bot_agent.rb +0 -278
- data/examples/tmux_chat/human_agent.rb +0 -199
- data/examples/tmux_chat/room_monitor.rb +0 -160
- data/examples/tmux_chat/shared_chat_system.rb +0 -328
- data/examples/tmux_chat/start_chat_demo.sh +0 -190
- data/examples/tmux_chat/stop_chat_demo.sh +0 -22
- /data/docs/{properties.md → core-concepts/properties.md} +0 -0
- /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
|