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,130 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<svg width="900" height="600" viewBox="0 0 900 600" xmlns="http://www.w3.org/2000/svg">
|
3
|
+
<defs>
|
4
|
+
<style>
|
5
|
+
.bg { fill: transparent; }
|
6
|
+
.health { fill: #1a3a2a; stroke: #4aff4a; stroke-width: 2; rx: 8; }
|
7
|
+
.fire { fill: #4a2a1a; stroke: #ffaa4a; stroke-width: 2; rx: 8; }
|
8
|
+
.police { fill: #1a2a3a; stroke: #4aafff; stroke-width: 2; rx: 8; }
|
9
|
+
.bank { fill: #3a2a1a; stroke: #ffcc4a; stroke-width: 2; rx: 8; }
|
10
|
+
.house { fill: #2a1a3a; stroke: #aa4aff; stroke-width: 2; rx: 8; }
|
11
|
+
.check-msg { stroke: #4aff4a; stroke-width: 2; fill: none; marker-end: url(#arrowhead-green); }
|
12
|
+
.status-msg { stroke: #ffaa4a; stroke-width: 2; fill: none; marker-end: url(#arrowhead-orange); stroke-dasharray: 5,3; }
|
13
|
+
.text { fill: #ffffff; font-family: 'Arial', sans-serif; font-size: 11px; }
|
14
|
+
.title { fill: #4a9eff; font-family: 'Arial', sans-serif; font-size: 18px; font-weight: bold; }
|
15
|
+
.subtitle { fill: #ffaa4a; font-family: 'Arial', sans-serif; font-size: 11px; }
|
16
|
+
.note { fill: #aaaaaa; font-family: 'Arial', sans-serif; font-size: 9px; }
|
17
|
+
</style>
|
18
|
+
<marker id="arrowhead-green" markerWidth="8" markerHeight="6"
|
19
|
+
refX="8" refY="3" orient="auto">
|
20
|
+
<polygon points="0 0, 8 3, 0 6" fill="#4aff4a" />
|
21
|
+
</marker>
|
22
|
+
<marker id="arrowhead-orange" markerWidth="8" markerHeight="6"
|
23
|
+
refX="8" refY="3" orient="auto">
|
24
|
+
<polygon points="0 0, 8 3, 0 6" fill="#ffaa4a" />
|
25
|
+
</marker>
|
26
|
+
</defs>
|
27
|
+
|
28
|
+
<!-- Background -->
|
29
|
+
<rect class="bg" width="900" height="600"/>
|
30
|
+
|
31
|
+
<!-- Title -->
|
32
|
+
<text x="450" y="25" text-anchor="middle" class="title">Health Monitoring System</text>
|
33
|
+
<text x="450" y="45" text-anchor="middle" class="subtitle">COVID-19 Compliant Service Health Checks (Every 5 seconds)</text>
|
34
|
+
|
35
|
+
<!-- Participants -->
|
36
|
+
<rect x="50" y="80" width="120" height="80" class="health"/>
|
37
|
+
<text x="110" y="105" text-anchor="middle" class="text">🏥 Health</text>
|
38
|
+
<text x="110" y="120" text-anchor="middle" class="text">Department</text>
|
39
|
+
<text x="110" y="140" text-anchor="middle" class="subtitle">Central Monitor</text>
|
40
|
+
<text x="110" y="155" text-anchor="middle" class="subtitle">COVID-19 Compliance</text>
|
41
|
+
|
42
|
+
<rect x="240" y="80" width="120" height="80" class="fire"/>
|
43
|
+
<text x="300" y="105" text-anchor="middle" class="text">🚒 Fire</text>
|
44
|
+
<text x="300" y="120" text-anchor="middle" class="text">Department</text>
|
45
|
+
<text x="300" y="140" text-anchor="middle" class="subtitle">Emergency Response</text>
|
46
|
+
|
47
|
+
<rect x="430" y="80" width="120" height="80" class="police"/>
|
48
|
+
<text x="490" y="105" text-anchor="middle" class="text">👮 Police</text>
|
49
|
+
<text x="490" y="120" text-anchor="middle" class="text">Department</text>
|
50
|
+
<text x="490" y="140" text-anchor="middle" class="subtitle">Law Enforcement</text>
|
51
|
+
|
52
|
+
<rect x="620" y="80" width="120" height="80" class="bank"/>
|
53
|
+
<text x="680" y="105" text-anchor="middle" class="text">🏦 Local</text>
|
54
|
+
<text x="680" y="120" text-anchor="middle" class="text">Bank</text>
|
55
|
+
<text x="680" y="140" text-anchor="middle" class="subtitle">Financial Security</text>
|
56
|
+
|
57
|
+
<rect x="810" y="80" width="80" height="80" class="house"/>
|
58
|
+
<text x="850" y="105" text-anchor="middle" class="text">🏠 House</text>
|
59
|
+
<text x="850" y="125" text-anchor="middle" class="subtitle">Residential</text>
|
60
|
+
<text x="850" y="140" text-anchor="middle" class="subtitle">Safety</text>
|
61
|
+
|
62
|
+
<!-- Vertical lifelines -->
|
63
|
+
<line x1="110" y1="160" x2="110" y2="520" stroke="#666" stroke-width="2" stroke-dasharray="3,3"/>
|
64
|
+
<line x1="300" y1="160" x2="300" y2="520" stroke="#666" stroke-width="2" stroke-dasharray="3,3"/>
|
65
|
+
<line x1="490" y1="160" x2="490" y2="520" stroke="#666" stroke-width="2" stroke-dasharray="3,3"/>
|
66
|
+
<line x1="680" y1="160" x2="680" y2="520" stroke="#666" stroke-width="2" stroke-dasharray="3,3"/>
|
67
|
+
<line x1="850" y1="160" x2="850" y2="520" stroke="#666" stroke-width="2" stroke-dasharray="3,3"/>
|
68
|
+
|
69
|
+
<!-- Health check broadcast note -->
|
70
|
+
<rect x="50" y="180" width="120" height="30" fill="#1a3a2a" stroke="#4aff4a" rx="4"/>
|
71
|
+
<text x="110" y="200" text-anchor="middle" class="note">Every 5 seconds</text>
|
72
|
+
|
73
|
+
<!-- Health check messages (broadcast to all) -->
|
74
|
+
<path d="M 110 220 L 300 220" class="check-msg"/>
|
75
|
+
<path d="M 110 240 L 490 240" class="check-msg"/>
|
76
|
+
<path d="M 110 260 L 680 260" class="check-msg"/>
|
77
|
+
<path d="M 110 280 L 850 280" class="check-msg"/>
|
78
|
+
|
79
|
+
<text x="205" y="215" text-anchor="middle" class="text">HealthCheckMessage</text>
|
80
|
+
<text x="300" y="235" text-anchor="middle" class="text">HealthCheckMessage</text>
|
81
|
+
<text x="395" y="255" text-anchor="middle" class="text">HealthCheckMessage</text>
|
82
|
+
<text x="480" y="275" text-anchor="middle" class="text">HealthCheckMessage</text>
|
83
|
+
|
84
|
+
<!-- Health status responses -->
|
85
|
+
<path d="M 300 320 L 110 320" class="status-msg"/>
|
86
|
+
<path d="M 490 340 L 110 340" class="status-msg"/>
|
87
|
+
<path d="M 680 360 L 110 360" class="status-msg"/>
|
88
|
+
<path d="M 850 380 L 110 380" class="status-msg"/>
|
89
|
+
|
90
|
+
<text x="205" y="315" text-anchor="middle" class="text">HealthStatusMessage</text>
|
91
|
+
<text x="300" y="335" text-anchor="middle" class="text">HealthStatusMessage</text>
|
92
|
+
<text x="395" y="355" text-anchor="middle" class="text">HealthStatusMessage</text>
|
93
|
+
<text x="480" y="375" text-anchor="middle" class="text">HealthStatusMessage</text>
|
94
|
+
|
95
|
+
<!-- Status indicators -->
|
96
|
+
<circle cx="300" cy="310" r="6" fill="#4aff4a"/>
|
97
|
+
<text x="320" y="315" class="note">Green</text>
|
98
|
+
|
99
|
+
<circle cx="490" cy="330" r="6" fill="#ffff4a"/>
|
100
|
+
<text x="510" y="335" class="note">Yellow</text>
|
101
|
+
|
102
|
+
<circle cx="680" cy="350" r="6" fill="#4aff4a"/>
|
103
|
+
<text x="700" y="355" class="note">Green</text>
|
104
|
+
|
105
|
+
<circle cx="850" cy="370" r="6" fill="#ff4a4a"/>
|
106
|
+
<text x="820" y="375" class="note">Red</text>
|
107
|
+
|
108
|
+
<!-- Countdown timer notes -->
|
109
|
+
<rect x="200" y="420" width="500" height="60" fill="#1a1a1a" stroke="#ffaa4a" rx="6"/>
|
110
|
+
<text x="210" y="440" class="text">10-second Countdown Timer System:</text>
|
111
|
+
<text x="210" y="455" class="note">• Resets on each HealthCheckMessage received</text>
|
112
|
+
<text x="210" y="470" class="note">• Services shut down after 10 seconds without health check (COVID-19 protocol)</text>
|
113
|
+
<text x="210" y="485" class="note">• Status: Green (healthy), Yellow (warning), Red (critical/offline)</text>
|
114
|
+
|
115
|
+
<!-- Service status legend -->
|
116
|
+
<rect x="50" y="500" width="840" height="80" fill="#2a2a2a" stroke="#4a9eff" rx="6"/>
|
117
|
+
<text x="70" y="520" class="title">Service Health Status Indicators:</text>
|
118
|
+
|
119
|
+
<circle cx="90" cy="540" r="8" fill="#4aff4a"/>
|
120
|
+
<text x="110" y="545" class="text">Green: Service healthy and responsive</text>
|
121
|
+
|
122
|
+
<circle cx="320" cy="540" r="8" fill="#ffff4a"/>
|
123
|
+
<text x="340" y="545" class="text">Yellow: Service degraded or slow response</text>
|
124
|
+
|
125
|
+
<circle cx="580" cy="540" r="8" fill="#ff4a4a"/>
|
126
|
+
<text x="600" y="545" class="text">Red: Service critical or unresponsive</text>
|
127
|
+
|
128
|
+
<text x="70" y="565" class="text">All services implement countdown timer health monitoring for COVID-19 compliance</text>
|
129
|
+
<text x="70" y="580" class="text">Health Department broadcasts HealthCheckMessage every 5 seconds to maintain service operation</text>
|
130
|
+
</svg>
|
@@ -0,0 +1,244 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/multi_program_demo/house.rb
|
3
|
+
|
4
|
+
require_relative '../../lib/smart_message'
|
5
|
+
require_relative 'messages/fire_emergency_message'
|
6
|
+
require_relative 'messages/fire_dispatch_message'
|
7
|
+
|
8
|
+
require_relative 'common/health_monitor'
|
9
|
+
require_relative 'common/logger'
|
10
|
+
|
11
|
+
class House
|
12
|
+
include Common::HealthMonitor
|
13
|
+
include Common::Logger
|
14
|
+
|
15
|
+
def initialize(address = nil)
|
16
|
+
@address = address || generate_random_address
|
17
|
+
@service_name = "house-#{@address.gsub(/\s+/, '-').downcase}"
|
18
|
+
@status = 'healthy'
|
19
|
+
@start_time = Time.now
|
20
|
+
@fire_active = false
|
21
|
+
@last_fire_time = nil
|
22
|
+
@occupants = rand(1..4)
|
23
|
+
@occupant_status = 'safe'
|
24
|
+
|
25
|
+
setup_messaging
|
26
|
+
setup_signal_handlers
|
27
|
+
setup_health_monitor
|
28
|
+
end
|
29
|
+
|
30
|
+
# def setup_logging
|
31
|
+
# # Create log file name based on address (sanitized)
|
32
|
+
# address_slug = @address.gsub(/\s+/, '_').gsub(/[^\w\-_]/, '').downcase
|
33
|
+
# log_file = File.join(__dir__, "house_#{address_slug}.log")
|
34
|
+
# logger = Logger.new(log_file)
|
35
|
+
# logger.level = Logger::INFO
|
36
|
+
# logger.formatter = proc do |severity, datetime, progname, msg|
|
37
|
+
# "#{datetime.strftime('%Y-%m-%d %H:%M:%S')} [#{severity}] #{msg}\n"
|
38
|
+
# end
|
39
|
+
# logger.info("House at #{@address} logging started")
|
40
|
+
# end
|
41
|
+
|
42
|
+
def setup_messaging
|
43
|
+
Messages::FireEmergencyMessage.from = @service_name
|
44
|
+
|
45
|
+
# Subscribe to fire dispatch responses
|
46
|
+
Messages::FireDispatchMessage.subscribe(to: @service_name) do |message|
|
47
|
+
handle_fire_response(message)
|
48
|
+
end
|
49
|
+
|
50
|
+
address_slug = @address.gsub(/\s+/, '_').gsub(/[^\w\-_]/, '').downcase
|
51
|
+
puts "🏠 House at #{@address} monitoring systems active"
|
52
|
+
puts " Occupants: #{@occupants}"
|
53
|
+
puts " Status: #{@occupant_status}"
|
54
|
+
puts " Press Ctrl+C to shutdown monitoring\n\n"
|
55
|
+
logger.info("House monitoring started: #{@occupants} occupants, status: #{@occupant_status}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def setup_signal_handlers
|
59
|
+
%w[INT TERM].each do |signal|
|
60
|
+
Signal.trap(signal) do
|
61
|
+
puts "\n🏠 #{@address} monitoring system shutting down..."
|
62
|
+
logger.info("House monitoring system shutting down")
|
63
|
+
exit(0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def start_monitoring
|
70
|
+
loop do
|
71
|
+
simulate_house_activity
|
72
|
+
sleep(rand(15..45)) # Random events every 15-45 seconds
|
73
|
+
end
|
74
|
+
rescue => e
|
75
|
+
puts "🏠 Error in house monitoring: #{e.message}"
|
76
|
+
logger.error("Error in house monitoring: #{e.message}")
|
77
|
+
retry
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def service_emoji
|
83
|
+
"🏠"
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_random_address
|
87
|
+
street_numbers = (100..999).to_a
|
88
|
+
street_names = [
|
89
|
+
'Oak Street', 'Maple Avenue', 'Pine Lane', 'Cedar Drive', 'Elm Court',
|
90
|
+
'Birch Road', 'Willow Way', 'Ash Boulevard', 'Cherry Street', 'Poplar Lane'
|
91
|
+
]
|
92
|
+
"#{street_numbers.sample} #{street_names.sample}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_status_details
|
96
|
+
# Status depends on fire situation and occupant safety
|
97
|
+
@status = if @fire_active
|
98
|
+
'critical'
|
99
|
+
elsif @occupant_status.include?('trapped') || @occupant_status.include?('rescued')
|
100
|
+
'warning'
|
101
|
+
else
|
102
|
+
'healthy'
|
103
|
+
end
|
104
|
+
|
105
|
+
details = case @status
|
106
|
+
when 'healthy' then "All systems normal, #{@occupants} occupants #{@occupant_status}"
|
107
|
+
when 'warning' then "Emergency response active, #{@occupants} occupants #{@occupant_status}"
|
108
|
+
when 'critical' then "FIRE DETECTED - #{@occupants} occupants status: #{@occupant_status}"
|
109
|
+
when 'failed' then "Power outage, backup systems failed"
|
110
|
+
end
|
111
|
+
|
112
|
+
[@status, details]
|
113
|
+
end
|
114
|
+
|
115
|
+
def simulate_house_activity
|
116
|
+
# Only start fires occasionally and not too frequently
|
117
|
+
return if @fire_active
|
118
|
+
return if @last_fire_time && (Time.now - @last_fire_time) < 600 # 10 minutes minimum between fires
|
119
|
+
|
120
|
+
# 6% chance of fire
|
121
|
+
if rand(100) < 6
|
122
|
+
start_fire
|
123
|
+
else
|
124
|
+
# Normal house activities
|
125
|
+
activities = [
|
126
|
+
'Security system armed',
|
127
|
+
'Temperature adjusted',
|
128
|
+
'Motion detected - normal activity',
|
129
|
+
'Smoke detector test completed',
|
130
|
+
'Door/window sensor check',
|
131
|
+
'All systems operational'
|
132
|
+
]
|
133
|
+
activity = activities.sample
|
134
|
+
puts "🏠 #{activity}"
|
135
|
+
logger.info("Normal activity: #{activity}")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def start_fire
|
140
|
+
@fire_active = true
|
141
|
+
@last_fire_time = Time.now
|
142
|
+
|
143
|
+
fire_types = ['kitchen', 'electrical', 'basement', 'garage']
|
144
|
+
severities = ['small', 'medium', 'large']
|
145
|
+
|
146
|
+
# Garage fires tend to be more severe
|
147
|
+
fire_type = fire_types.sample
|
148
|
+
severity = if fire_type == 'garage'
|
149
|
+
['medium', 'large', 'out_of_control'].sample
|
150
|
+
else
|
151
|
+
severities.sample
|
152
|
+
end
|
153
|
+
|
154
|
+
# Determine occupant status based on fire severity and time of day
|
155
|
+
hour = Time.now.hour
|
156
|
+
if severity == 'out_of_control' || (hour >= 22 || hour <= 6) # Night time
|
157
|
+
@occupant_status = ['safe - evacuated', 'trapped - need rescue'].sample
|
158
|
+
else
|
159
|
+
@occupant_status = 'safe - evacuated'
|
160
|
+
end
|
161
|
+
|
162
|
+
# Determine spread risk
|
163
|
+
spread_risk = case severity
|
164
|
+
when 'small' then 'Low risk to neighboring structures'
|
165
|
+
when 'medium' then 'Moderate risk if not contained quickly'
|
166
|
+
when 'large' then 'High risk to adjacent buildings'
|
167
|
+
when 'out_of_control' then 'EXTREME RISK - evacuate neighbors'
|
168
|
+
end
|
169
|
+
|
170
|
+
emergency = Messages::FireEmergencyMessage.new(
|
171
|
+
house_address: @address,
|
172
|
+
fire_type: fire_type,
|
173
|
+
severity: severity,
|
174
|
+
timestamp: Time.now.strftime('%Y-%m-%d %H:%M:%S'),
|
175
|
+
occupants_status: @occupant_status,
|
176
|
+
spread_risk: spread_risk,
|
177
|
+
)
|
178
|
+
emergency.publish
|
179
|
+
|
180
|
+
puts "🏠 🔥 FIRE EMERGENCY 🔥"
|
181
|
+
puts " Address: #{@address}"
|
182
|
+
puts " Type: #{fire_type.upcase}"
|
183
|
+
puts " Severity: #{severity.upcase}"
|
184
|
+
puts " Occupants: #{@occupant_status}"
|
185
|
+
puts " Spread Risk: #{spread_risk}"
|
186
|
+
logger.warn("FIRE EMERGENCY: #{fire_type} fire (#{severity}) - Occupants: #{@occupant_status}")
|
187
|
+
rescue => e
|
188
|
+
puts "🏠 Error starting fire emergency: #{e.message}"
|
189
|
+
logger.error("Error starting fire emergency: #{e.message}")
|
190
|
+
end
|
191
|
+
|
192
|
+
def handle_fire_response(dispatch)
|
193
|
+
# Process fire dispatch message
|
194
|
+
puts "🚒 \e[31mFIRE DISPATCH\e[0m ##{dispatch.dispatch_id}"
|
195
|
+
puts " Engines: #{dispatch.engines_assigned.join(', ')}"
|
196
|
+
puts " Location: #{dispatch.location}"
|
197
|
+
puts " Fire Type: #{dispatch.fire_type}"
|
198
|
+
puts " Equipment: #{dispatch.equipment_needed}" if dispatch.equipment_needed
|
199
|
+
puts " ETA: #{dispatch.estimated_arrival}" if dispatch.estimated_arrival
|
200
|
+
puts " Dispatched: #{dispatch.timestamp}"
|
201
|
+
|
202
|
+
puts "🏠 Fire department response received"
|
203
|
+
puts " #{dispatch.engines_assigned.size} engines dispatched"
|
204
|
+
puts " ETA: #{dispatch.estimated_arrival}"
|
205
|
+
puts " Equipment: #{dispatch.equipment_needed}"
|
206
|
+
logger.info("Fire department response: #{dispatch.engines_assigned.join(', ')} dispatched, ETA #{dispatch.estimated_arrival}")
|
207
|
+
|
208
|
+
# If occupants were trapped, fire department rescues them
|
209
|
+
if @occupant_status.include?('trapped')
|
210
|
+
@occupant_status = 'rescued by fire department'
|
211
|
+
puts "🏠 🚒 Occupants successfully rescued!"
|
212
|
+
logger.info("Occupants rescued by fire department")
|
213
|
+
end
|
214
|
+
|
215
|
+
# Simulate fire resolution when fire department arrives
|
216
|
+
Thread.new do
|
217
|
+
sleep(rand(300..900)) # 5-15 minutes for fire suppression
|
218
|
+
extinguish_fire
|
219
|
+
end
|
220
|
+
rescue => e
|
221
|
+
puts "🏠 Error handling fire response: #{e.message}"
|
222
|
+
logger.error("Error handling fire response: #{e.message}")
|
223
|
+
end
|
224
|
+
|
225
|
+
def extinguish_fire
|
226
|
+
@fire_active = false
|
227
|
+
@occupant_status = 'safe'
|
228
|
+
|
229
|
+
puts "🏠 ✅ Fire extinguished by fire department"
|
230
|
+
puts " All occupants accounted for and safe"
|
231
|
+
puts " House systems returning to normal"
|
232
|
+
logger.info("Fire extinguished, all occupants safe, returning to normal")
|
233
|
+
rescue => e
|
234
|
+
puts "🏠 Error extinguishing fire: #{e.message}"
|
235
|
+
logger.error("Error extinguishing fire: #{e.message}")
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
if __FILE__ == $0
|
240
|
+
# Allow specifying address as command line argument
|
241
|
+
address = ARGV[0]
|
242
|
+
house = House.new(address)
|
243
|
+
house.start_monitoring
|
244
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/multi_program_demo/local_bank.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
|
+
|
10
|
+
require_relative 'common/health_monitor'
|
11
|
+
require_relative 'common/logger'
|
12
|
+
|
13
|
+
class LocalBank
|
14
|
+
include Common::HealthMonitor
|
15
|
+
include Common::Logger
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@service_name = 'first-national-bank'
|
19
|
+
@location = '123 Main Street'
|
20
|
+
@status = 'healthy'
|
21
|
+
@start_time = Time.now
|
22
|
+
@alarm_active = false
|
23
|
+
@last_alarm_time = nil
|
24
|
+
@security_level = 'normal'
|
25
|
+
|
26
|
+
setup_messaging
|
27
|
+
setup_signal_handlers
|
28
|
+
setup_health_monitor
|
29
|
+
end
|
30
|
+
|
31
|
+
# def setup_logging
|
32
|
+
# log_file = File.join(__dir__, 'local_bank.log')
|
33
|
+
# logger = Logger.new(log_file)
|
34
|
+
# logger.level = Logger::INFO
|
35
|
+
# logger.formatter = proc do |severity, datetime, progname, msg|
|
36
|
+
# "#{datetime.strftime('%Y-%m-%d %H:%M:%S')} [#{severity}] #{msg}\n"
|
37
|
+
# end
|
38
|
+
# logger.info("Local Bank logging started")
|
39
|
+
# end
|
40
|
+
|
41
|
+
def setup_messaging
|
42
|
+
# Subscribe to police dispatch responses
|
43
|
+
Messages::PoliceDispatchMessage.subscribe(to: @service_name) do |message|
|
44
|
+
handle_police_response(message)
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "🏦 #{@service_name} open for business"
|
48
|
+
puts " Location: #{@location}"
|
49
|
+
puts " Security level: #{@security_level}"
|
50
|
+
puts " Logging to: local_bank.log"
|
51
|
+
puts " Press Ctrl+C to close\n\n"
|
52
|
+
logger.info("#{@service_name} open for business at #{@location}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_signal_handlers
|
56
|
+
%w[INT TERM].each do |signal|
|
57
|
+
Signal.trap(signal) do
|
58
|
+
puts "\n🏦 #{@service_name} closing for the day..."
|
59
|
+
logger.info("#{@service_name} closing for the day")
|
60
|
+
exit(0)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def start_operations
|
67
|
+
loop do
|
68
|
+
simulate_bank_activity
|
69
|
+
sleep(rand(10..30)) # Random activities every 10-30 seconds
|
70
|
+
end
|
71
|
+
rescue => e
|
72
|
+
puts "🏦 Error in bank operations: #{e.message}"
|
73
|
+
logger.error("Error in bank operations: #{e.message}")
|
74
|
+
retry
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def service_emoji
|
80
|
+
"🏦"
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_status_details
|
84
|
+
# Determine status based on security level and alarm state
|
85
|
+
@status = if @alarm_active
|
86
|
+
'critical'
|
87
|
+
elsif @security_level == 'high'
|
88
|
+
'warning'
|
89
|
+
else
|
90
|
+
'healthy'
|
91
|
+
end
|
92
|
+
|
93
|
+
details = case @status
|
94
|
+
when 'healthy' then "All systems operational, security level: #{@security_level}"
|
95
|
+
when 'warning' then "Elevated security, monitoring active"
|
96
|
+
when 'critical' then "Security breach detected, lockdown protocols active"
|
97
|
+
when 'failed' then "Systems offline, emergency procedures in effect"
|
98
|
+
end
|
99
|
+
|
100
|
+
[@status, details]
|
101
|
+
end
|
102
|
+
|
103
|
+
def simulate_bank_activity
|
104
|
+
# Only trigger alarms occasionally and not too frequently
|
105
|
+
return if @alarm_active
|
106
|
+
return if @last_alarm_time && (Time.now - @last_alarm_time) < 300 # 5 minutes minimum between alarms
|
107
|
+
|
108
|
+
# 8% chance of suspicious activity
|
109
|
+
if rand(100) < 8
|
110
|
+
trigger_silent_alarm
|
111
|
+
else
|
112
|
+
# Normal bank operations
|
113
|
+
activities = [
|
114
|
+
'Customer transaction completed',
|
115
|
+
'ATM refilled',
|
116
|
+
'Vault inspection completed',
|
117
|
+
'Security patrol completed',
|
118
|
+
'System backup completed'
|
119
|
+
]
|
120
|
+
activity = activities.sample
|
121
|
+
puts "🏦 #{activity}"
|
122
|
+
logger.info("Normal operation: #{activity}")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def trigger_silent_alarm
|
127
|
+
@alarm_active = true
|
128
|
+
@last_alarm_time = Time.now
|
129
|
+
@security_level = 'high'
|
130
|
+
|
131
|
+
alarm_types = ['robbery', 'vault_breach', 'suspicious_activity']
|
132
|
+
severities = ['medium', 'high', 'critical']
|
133
|
+
|
134
|
+
alarm_type = alarm_types.sample
|
135
|
+
severity = alarm_type == 'robbery' ? 'critical' : severities.sample
|
136
|
+
|
137
|
+
details = case alarm_type
|
138
|
+
when 'robbery'
|
139
|
+
'Armed individuals at teller window, customers on floor'
|
140
|
+
when 'vault_breach'
|
141
|
+
'Unauthorized access attempt detected in vault area'
|
142
|
+
when 'suspicious_activity'
|
143
|
+
'Multiple individuals casing the building, unusual behavior'
|
144
|
+
end
|
145
|
+
|
146
|
+
alarm = Messages::SilentAlarmMessage.new(
|
147
|
+
bank_name: @service_name,
|
148
|
+
location: @location,
|
149
|
+
alarm_type: alarm_type,
|
150
|
+
timestamp: Time.now.strftime('%Y-%m-%d %H:%M:%S'),
|
151
|
+
severity: severity,
|
152
|
+
details: details
|
153
|
+
)
|
154
|
+
alarm._sm_from = @service_name
|
155
|
+
alarm._sm_to = 'police-department'
|
156
|
+
alarm.publish
|
157
|
+
|
158
|
+
puts "🏦 🚨 SILENT ALARM TRIGGERED 🚨"
|
159
|
+
puts " Type: #{alarm_type.upcase}"
|
160
|
+
puts " Severity: #{severity.upcase}"
|
161
|
+
puts " Details: #{details}"
|
162
|
+
logger.warn("SILENT ALARM TRIGGERED: #{alarm_type} (#{severity}) - #{details}")
|
163
|
+
rescue => e
|
164
|
+
puts "🏦 Error triggering silent alarm: #{e.message}"
|
165
|
+
logger.error("Error triggering silent alarm: #{e.message}")
|
166
|
+
end
|
167
|
+
|
168
|
+
def handle_police_response(dispatch)
|
169
|
+
# Process police dispatch message with colored output
|
170
|
+
priority_color = case dispatch.priority
|
171
|
+
when 'low' then "\e[32m" # Green
|
172
|
+
when 'medium' then "\e[33m" # Yellow
|
173
|
+
when 'high' then "\e[93m" # Orange
|
174
|
+
when 'emergency' then "\e[31m" # Red
|
175
|
+
else "\e[0m"
|
176
|
+
end
|
177
|
+
|
178
|
+
puts "🚔 #{priority_color}POLICE DISPATCH\e[0m ##{dispatch.dispatch_id}"
|
179
|
+
puts " Units: #{dispatch.units_assigned.join(', ')}"
|
180
|
+
puts " Location: #{dispatch.location}"
|
181
|
+
puts " Incident: #{dispatch.incident_type} (#{dispatch.priority.upcase} priority)"
|
182
|
+
puts " ETA: #{dispatch.estimated_arrival}" if dispatch.estimated_arrival
|
183
|
+
puts " Dispatched: #{dispatch.timestamp}"
|
184
|
+
|
185
|
+
puts "🏦 Police response received - #{dispatch.units_assigned.size} units dispatched"
|
186
|
+
puts " ETA: #{dispatch.estimated_arrival}"
|
187
|
+
puts " Implementing security protocols..."
|
188
|
+
logger.info("Police response received: #{dispatch.units_assigned.join(', ')} dispatched, ETA #{dispatch.estimated_arrival}")
|
189
|
+
|
190
|
+
# Simulate resolution after police arrive
|
191
|
+
Thread.new do
|
192
|
+
sleep(rand(180..600)) # 3-10 minutes for resolution
|
193
|
+
resolve_security_incident
|
194
|
+
end
|
195
|
+
rescue => e
|
196
|
+
puts "🏦 Error handling police response: #{e.message}"
|
197
|
+
logger.error("Error handling police response: #{e.message}")
|
198
|
+
end
|
199
|
+
|
200
|
+
def resolve_security_incident
|
201
|
+
@alarm_active = false
|
202
|
+
@security_level = 'normal'
|
203
|
+
|
204
|
+
puts "🏦 ✅ Security incident resolved"
|
205
|
+
puts " Returning to normal operations"
|
206
|
+
puts " Security level: #{@security_level}"
|
207
|
+
logger.info("Security incident resolved, returning to normal operations")
|
208
|
+
rescue => e
|
209
|
+
puts "🏦 Error resolving security incident: #{e.message}"
|
210
|
+
logger.error("Error resolving security incident: #{e.message}")
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
if __FILE__ == $0
|
215
|
+
bank = LocalBank.new
|
216
|
+
bank.start_operations
|
217
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/messages/emergency_911_message.rb
|
3
|
+
|
4
|
+
require_relative '../../../lib/smart_message'
|
5
|
+
|
6
|
+
module Messages
|
7
|
+
class Emergency911Message < SmartMessage::Base
|
8
|
+
version 1
|
9
|
+
|
10
|
+
description 'Emergency 911 call for reporting fires, crimes, accidents, medical emergencies, or other urgent situations requiring police, fire, or medical response'
|
11
|
+
|
12
|
+
transport SmartMessage::Transport::RedisTransport.new
|
13
|
+
serializer SmartMessage::Serializer::Json.new
|
14
|
+
|
15
|
+
VALID_EMERGENCY_TYPES = %w[fire medical crime accident hazmat rescue other]
|
16
|
+
VALID_SEVERITY = %w[critical high medium low]
|
17
|
+
|
18
|
+
# Caller information
|
19
|
+
property :caller_name,
|
20
|
+
description: 'Name of the person calling 911'
|
21
|
+
|
22
|
+
property :caller_phone,
|
23
|
+
description: 'Phone number of the caller'
|
24
|
+
|
25
|
+
property :caller_location,
|
26
|
+
required: true,
|
27
|
+
description: 'Location of the emergency (address or description)'
|
28
|
+
|
29
|
+
property :emergency_type,
|
30
|
+
required: true,
|
31
|
+
validate: ->(v) { VALID_EMERGENCY_TYPES.include?(v) },
|
32
|
+
validation_message: "Emergency type must be: #{VALID_EMERGENCY_TYPES.join(', ')}",
|
33
|
+
description: "Type of emergency being reported. Valid values: #{VALID_EMERGENCY_TYPES.join(', ')}"
|
34
|
+
|
35
|
+
property :description,
|
36
|
+
required: true,
|
37
|
+
description: 'Detailed description of the emergency'
|
38
|
+
|
39
|
+
property :severity,
|
40
|
+
validate: ->(v) { VALID_SEVERITY.include?(v) },
|
41
|
+
validation_message: "Severity must be: #{VALID_SEVERITY.join(', ')}",
|
42
|
+
description: "Perceived severity of the emergency. Valid values: #{VALID_SEVERITY.join(', ')}"
|
43
|
+
|
44
|
+
property :injuries_reported,
|
45
|
+
description: 'Are there injuries? (true/false)'
|
46
|
+
|
47
|
+
property :number_of_victims,
|
48
|
+
description: 'Number of people affected or injured'
|
49
|
+
|
50
|
+
property :fire_involved,
|
51
|
+
description: 'Is there fire involved? (true/false)'
|
52
|
+
|
53
|
+
property :weapons_involved,
|
54
|
+
description: 'Are weapons involved? (true/false)'
|
55
|
+
|
56
|
+
property :hazardous_materials,
|
57
|
+
description: 'Are hazardous materials involved? (true/false)'
|
58
|
+
|
59
|
+
property :vehicles_involved,
|
60
|
+
description: 'Number of vehicles involved (for accidents)'
|
61
|
+
|
62
|
+
property :suspects_on_scene,
|
63
|
+
description: 'Are suspects still on scene? (true/false)'
|
64
|
+
|
65
|
+
property :additional_info,
|
66
|
+
description: 'Any additional information provided by caller'
|
67
|
+
|
68
|
+
property :dispatch_to,
|
69
|
+
description: 'Which departments to dispatch to (array of department names)'
|
70
|
+
|
71
|
+
property :priority,
|
72
|
+
description: 'Dispatch priority level (1-5, 1 being highest)'
|
73
|
+
|
74
|
+
property :call_id,
|
75
|
+
description: 'Unique identifier for this 911 call'
|
76
|
+
|
77
|
+
property :call_received_at,
|
78
|
+
default: -> { Time.now.iso8601 },
|
79
|
+
description: 'When the 911 call was received'
|
80
|
+
end
|
81
|
+
end
|