cirrocumulus 0.4.6 → 0.5.2
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.
- data/Gemfile +1 -1
- data/LICENSE.txt +502 -0
- data/VERSION +1 -1
- data/cirrocumulus.gemspec +14 -15
- data/lib/cirrocumulus/agent.rb +97 -37
- data/lib/cirrocumulus/agent_wrapper.rb +68 -0
- data/lib/cirrocumulus/engine.rb +142 -63
- data/lib/cirrocumulus/ontology.rb +7 -2
- data/lib/cirrocumulus/rule_engine.rb +2 -259
- data/lib/cirrocumulus/rules/engine.rb +314 -0
- data/lib/cirrocumulus/rules/run_queue.rb +108 -0
- metadata +100 -156
- data/Gemfile.lock +0 -33
- data/lib/cirrocumulus/master_agent.rb +0 -205
- data/lib/test.rb +0 -56
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.2
|
data/cirrocumulus.gemspec
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.
|
7
|
+
s.name = "cirrocumulus"
|
8
|
+
s.version = "0.5.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Anton Kosyakin"]
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
12
|
+
s.date = "2011-12-01"
|
13
|
+
s.description = "Engine for building your own agents, providing you base functionality for loading ontologies, communicating with other agents and parsing FIPA-ACL messages"
|
14
|
+
s.email = "deil@mneko.net"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE.txt",
|
17
17
|
"README.rdoc"
|
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
21
|
"Gemfile",
|
22
|
-
"Gemfile.lock",
|
23
22
|
"LICENSE.txt",
|
24
23
|
"README.rdoc",
|
25
24
|
"Rakefile",
|
@@ -27,26 +26,26 @@ Gem::Specification.new do |s|
|
|
27
26
|
"cirrocumulus.gemspec",
|
28
27
|
"lib/cirrocumulus.rb",
|
29
28
|
"lib/cirrocumulus/agent.rb",
|
29
|
+
"lib/cirrocumulus/agent_wrapper.rb",
|
30
30
|
"lib/cirrocumulus/engine.rb",
|
31
31
|
"lib/cirrocumulus/kb.rb",
|
32
32
|
"lib/cirrocumulus/logger.rb",
|
33
|
-
"lib/cirrocumulus/master_agent.rb",
|
34
33
|
"lib/cirrocumulus/ontology.rb",
|
35
34
|
"lib/cirrocumulus/rule_engine.rb",
|
36
35
|
"lib/cirrocumulus/rule_server.rb",
|
36
|
+
"lib/cirrocumulus/rules/engine.rb",
|
37
|
+
"lib/cirrocumulus/rules/run_queue.rb",
|
37
38
|
"lib/cirrocumulus/saga.rb",
|
38
|
-
"lib/test.rb",
|
39
39
|
"test/helper.rb",
|
40
40
|
"test/test_cirrocumulus.rb"
|
41
41
|
]
|
42
|
-
s.homepage =
|
42
|
+
s.homepage = "http://github.com/deil/cirrocumulus"
|
43
43
|
s.licenses = ["GPL-2"]
|
44
44
|
s.require_paths = ["lib"]
|
45
|
-
s.rubygems_version =
|
46
|
-
s.summary =
|
45
|
+
s.rubygems_version = "1.8.10"
|
46
|
+
s.summary = "Agent-based infrastructure management system"
|
47
47
|
|
48
48
|
if s.respond_to? :specification_version then
|
49
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
49
|
s.specification_version = 3
|
51
50
|
|
52
51
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
@@ -54,7 +53,7 @@ Gem::Specification.new do |s|
|
|
54
53
|
s.add_runtime_dependency(%q<log4r>, ["~> 1.1.9"])
|
55
54
|
s.add_runtime_dependency(%q<systemu>, [">= 0"])
|
56
55
|
s.add_runtime_dependency(%q<xmpp4r>, ["~> 0.5"])
|
57
|
-
s.add_runtime_dependency(%q<xmpp4r-simple>, ["
|
56
|
+
s.add_runtime_dependency(%q<xmpp4r-simple>, [">= 0"])
|
58
57
|
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
59
58
|
s.add_runtime_dependency(%q<deil_sexpistol>, [">= 0"])
|
60
59
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -65,7 +64,7 @@ Gem::Specification.new do |s|
|
|
65
64
|
s.add_dependency(%q<log4r>, ["~> 1.1.9"])
|
66
65
|
s.add_dependency(%q<systemu>, [">= 0"])
|
67
66
|
s.add_dependency(%q<xmpp4r>, ["~> 0.5"])
|
68
|
-
s.add_dependency(%q<xmpp4r-simple>, ["
|
67
|
+
s.add_dependency(%q<xmpp4r-simple>, [">= 0"])
|
69
68
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
70
69
|
s.add_dependency(%q<deil_sexpistol>, [">= 0"])
|
71
70
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -77,7 +76,7 @@ Gem::Specification.new do |s|
|
|
77
76
|
s.add_dependency(%q<log4r>, ["~> 1.1.9"])
|
78
77
|
s.add_dependency(%q<systemu>, [">= 0"])
|
79
78
|
s.add_dependency(%q<xmpp4r>, ["~> 0.5"])
|
80
|
-
s.add_dependency(%q<xmpp4r-simple>, ["
|
79
|
+
s.add_dependency(%q<xmpp4r-simple>, [">= 0"])
|
81
80
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
82
81
|
s.add_dependency(%q<deil_sexpistol>, [">= 0"])
|
83
82
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
data/lib/cirrocumulus/agent.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Agent
|
2
4
|
class AgentInfo
|
3
5
|
attr_accessor :identifier
|
4
6
|
attr_accessor :default_ontology
|
@@ -135,48 +137,106 @@ class Agent
|
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
def initialize(cm)
|
143
|
-
@cm = cm
|
144
|
-
@identifier = cm.jid
|
145
|
-
@sagas = []
|
146
|
-
@saga_idx = 0
|
147
|
-
@network_map = NetworkMap.new(self)
|
148
|
-
end
|
140
|
+
class Base
|
141
|
+
attr_reader :identifier
|
142
|
+
attr_reader :network_map
|
149
143
|
|
150
|
-
|
151
|
-
|
144
|
+
def initialize(cm)
|
145
|
+
Log4r::Logger['agent'].info('Initializing new agent')
|
152
146
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
147
|
+
@cm = cm
|
148
|
+
@identifier = cm.jid
|
149
|
+
@ontologies = []
|
150
|
+
@message_queues = {}
|
151
|
+
@worker_threads = {}
|
152
|
+
#@network_map = NetworkMap.new(self)
|
157
153
|
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def restore_state()
|
161
|
-
end
|
162
154
|
|
163
|
-
|
164
|
-
|
165
|
-
|
155
|
+
# Loads ontologies into agent
|
156
|
+
def load_ontologies(ontologies_list)
|
157
|
+
ontologies_list.each do |ontology_name|
|
158
|
+
ontology = eval("#{ontology_name}.new(self)")
|
159
|
+
@ontologies << ontology
|
160
|
+
@message_queues[ontology] = Queue.new
|
161
|
+
end
|
162
|
+
end
|
166
163
|
|
167
|
-
|
168
|
-
|
164
|
+
def default_ontology
|
165
|
+
self.ontologies.size == 1 ? self.ontologies.first.name : nil
|
166
|
+
end
|
169
167
|
|
170
|
-
if
|
171
|
-
|
168
|
+
# Returns true if this agents supports requested ontology
|
169
|
+
def handles_ontology?(ontology_name)
|
170
|
+
self.ontologies.any? {|ontology| ontology.name == ontology_name}
|
171
|
+
end
|
172
|
+
|
173
|
+
# Asks each loaded ontology to restore its state
|
174
|
+
def restore_state()
|
175
|
+
Log4r::Logger['agent'].info 'Restoring previous state..'
|
176
|
+
|
177
|
+
threads = []
|
178
|
+
self.ontologies.each do |ontology|
|
179
|
+
threads << Thread.new do
|
180
|
+
begin
|
181
|
+
ontology.restore_state()
|
182
|
+
rescue Exception => e
|
183
|
+
Log4r::Logger['agent'].warn "failed to restore state for ontology %s" % ontology.name
|
184
|
+
Log4r::Logger['agent'].warn "%s: %s" % [e.to_s, e.backtrace.to_s]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
threads.each {|thrd| thrd.join}
|
190
|
+
Log4r::Logger['agent'].info 'State restored successfully!'
|
191
|
+
end
|
192
|
+
|
193
|
+
# Starts message processing threads
|
194
|
+
def start()
|
195
|
+
start_workers()
|
196
|
+
end
|
197
|
+
|
198
|
+
def send_message(message)
|
199
|
+
@cm.send_message(message)
|
200
|
+
end
|
201
|
+
|
202
|
+
def handle_message(message)
|
203
|
+
#@network_map.handle_message(message, @cm)
|
204
|
+
|
205
|
+
self.ontologies.each {|ontology|
|
206
|
+
if message.ontology == ontology.name || ontology.sagas.any? {|saga| saga.id == message.in_reply_to}
|
207
|
+
@message_queues[ontology] << message
|
208
|
+
break
|
209
|
+
end
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
protected
|
214
|
+
|
215
|
+
attr_reader :cm
|
216
|
+
attr_reader :ontologies
|
217
|
+
|
218
|
+
def start_workers()
|
219
|
+
self.ontologies.each do |ontology|
|
220
|
+
@worker_threads[ontology] = Thread.new do
|
221
|
+
Log4r::Logger['agent'].info "Starting worker for ontology %s" % [ontology.name]
|
222
|
+
process_ontology(ontology, @message_queues[ontology])
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def process_ontology(ontology, queue)
|
228
|
+
while true do
|
229
|
+
message = queue.pop(true) rescue nil
|
230
|
+
begin
|
231
|
+
ontology.handle_incoming_message(message, nil) if message
|
232
|
+
rescue Exception => e
|
233
|
+
Log4r::Logger['agent'].warn "failed to handle incoming message for ontology %s: %s" % [ontology.name, e.to_s]
|
234
|
+
Log4r::Logger['agent'].warn e.backtrace.to_s
|
235
|
+
end
|
236
|
+
|
237
|
+
ontology.tick()
|
238
|
+
sleep 0.5
|
239
|
+
end
|
172
240
|
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def self.get_node(sender)
|
176
|
-
sender.split('-').first
|
177
|
-
end
|
178
|
-
|
179
|
-
def self.get_subsystem(sender)
|
180
|
-
sender.split('-').second
|
181
241
|
end
|
182
242
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
unless Kernel.respond_to?(:require_relative)
|
2
|
+
module Kernel
|
3
|
+
def require_relative(path)
|
4
|
+
require File.join(File.dirname(caller[0]), path.to_str)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
require 'cirrocumulus'
|
11
|
+
require 'cirrocumulus/logger'
|
12
|
+
require 'cirrocumulus/engine'
|
13
|
+
require 'cirrocumulus/kb'
|
14
|
+
require 'cirrocumulus/ontology'
|
15
|
+
require 'cirrocumulus/agent'
|
16
|
+
|
17
|
+
class String
|
18
|
+
def underscore
|
19
|
+
self.gsub(/::/, '/').
|
20
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
21
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
22
|
+
tr("-", "_").
|
23
|
+
downcase
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ontologies_file_name = nil
|
28
|
+
|
29
|
+
ARGV.each_with_index do |arg, i|
|
30
|
+
if arg == '-c'
|
31
|
+
ontologies_file_name = ARGV[i + 1]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if ontologies_file_name.nil?
|
36
|
+
puts "Please supply config file name"
|
37
|
+
exit(0)
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "Loading configuration.."
|
41
|
+
agent_config = YAML.load_file(ontologies_file_name)
|
42
|
+
ontologies = agent_config['ontologies']
|
43
|
+
ontologies.each do |ontology_name|
|
44
|
+
puts "Will load ontology %s" % ontology_name
|
45
|
+
require File.join(AGENT_ROOT, 'ontologies', ontology_name.underscore)
|
46
|
+
end
|
47
|
+
|
48
|
+
kb_name = agent_config['kb']
|
49
|
+
kb = if kb_name
|
50
|
+
puts "Will load knowledge base %s" % kb_name
|
51
|
+
require File.join(AGENT_ROOT, 'ontologies/xen/', kb_name.underscore) # TODO
|
52
|
+
eval("#{kb_name}.new()")
|
53
|
+
else
|
54
|
+
Kb.new
|
55
|
+
end
|
56
|
+
|
57
|
+
cm = Cirrocumulus.new('master')
|
58
|
+
a = Agent::Base.new(cm)
|
59
|
+
a.load_ontologies(agent_config['ontologies'])
|
60
|
+
begin
|
61
|
+
cm.run(a, kb)
|
62
|
+
rescue Exception => e
|
63
|
+
puts 'Got an error:'
|
64
|
+
puts e
|
65
|
+
puts e.backtrace
|
66
|
+
end
|
67
|
+
|
68
|
+
puts "\nBye-bye."
|
data/lib/cirrocumulus/engine.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'systemu'
|
2
|
+
require 'thread'
|
2
3
|
|
3
4
|
class Cirrocumulus
|
5
|
+
# Message. Used by agents to communicate
|
4
6
|
class Message
|
5
7
|
attr_accessor :sender, :act, :content
|
6
|
-
attr_accessor :receiver, :reply_with, :in_reply_to
|
8
|
+
attr_accessor :receiver, :reply_with, :in_reply_to, :conversation_id
|
7
9
|
attr_accessor :ontology
|
8
10
|
|
9
11
|
def initialize(sender, act, content)
|
@@ -19,22 +21,58 @@ class Cirrocumulus
|
|
19
21
|
def context
|
20
22
|
Context.new(@sender, @reply_with)
|
21
23
|
end
|
24
|
+
|
25
|
+
def self.parse_params(content, subroutine = false)
|
26
|
+
return parse_params(content.size == 1 ? content[0] : content, true) if !subroutine
|
27
|
+
|
28
|
+
return [] if content.nil?
|
29
|
+
return content if !content.is_a?(Array)
|
30
|
+
return [] if content.size == 0
|
31
|
+
return {content[0] => []} if content.size == 1
|
32
|
+
return {content[0] => parse_params(content[1], true)} if content.size == 2
|
33
|
+
|
34
|
+
res = {content[0] => []}
|
35
|
+
|
36
|
+
if content.all? {|item| !item.is_a?(Array)}
|
37
|
+
content.each_with_index do |item,i|
|
38
|
+
if i == 0
|
39
|
+
res[content[0]] = []
|
40
|
+
else
|
41
|
+
res[content[0]] << item
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
content.each_with_index do |item,i|
|
46
|
+
if i == 0
|
47
|
+
res[content[0]] = {}
|
48
|
+
else
|
49
|
+
res[content[0]].merge!(parse_params(item, true))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
res
|
55
|
+
end
|
22
56
|
end
|
23
57
|
|
58
|
+
# Message context. Includes sender, reply-with and conversation-id
|
24
59
|
class Context
|
25
60
|
attr_reader :sender
|
26
61
|
attr_reader :reply_with
|
62
|
+
attr_reader :conversation_id
|
27
63
|
|
28
|
-
def initialize(sender, reply_with)
|
64
|
+
def initialize(sender, reply_with, conversation_id = nil)
|
29
65
|
@sender = sender
|
30
66
|
@reply_with = reply_with
|
67
|
+
@conversation_id = conversation_id
|
31
68
|
end
|
32
69
|
end
|
33
70
|
|
71
|
+
# Gets current platform (linux, freebsd, etc)
|
34
72
|
def self.platform
|
35
|
-
if
|
73
|
+
if RUBY_PLATFORM =~ /freebsd/
|
36
74
|
return 'freebsd'
|
37
|
-
elsif
|
75
|
+
elsif RUBY_PLATFORM =~ /linux/
|
38
76
|
return 'linux'
|
39
77
|
end
|
40
78
|
|
@@ -47,20 +85,15 @@ class Cirrocumulus
|
|
47
85
|
Log4r::Logger['cirrocumulus'].info 'platform: ' + Cirrocumulus::platform
|
48
86
|
@suffix = suffix
|
49
87
|
@generate_jid = generate_jid
|
88
|
+
@send_queue = Queue.new
|
50
89
|
end
|
51
90
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
msg += " act=\"#{message.act}\""
|
56
|
-
msg += " reply-with=\"#{message.reply_with}\"" if message.reply_with
|
57
|
-
msg += " in-reply-to=\"#{message.in_reply_to}\"" if message.in_reply_to
|
58
|
-
message_content = message.content if message.content.is_a? String
|
59
|
-
message_content = Sexpistol.new.to_sexp(message.content) if message.content.is_a? Array
|
60
|
-
msg += "><content>#{message_content}</content></fipa-message>"
|
61
|
-
@im.send!("<message type=\"groupchat\" to=\"#{JABBER_CONFERENCE}\" id=\"aaefa\"><body>#{msg.gsub('&', '&').gsub('<', '<').gsub('>', '>')}</body></message>")
|
91
|
+
# Sends message to other agents
|
92
|
+
def send_message(message)
|
93
|
+
@send_queue << message
|
62
94
|
end
|
63
95
|
|
96
|
+
=begin
|
64
97
|
def inform(receiver, answer, ontology = nil)
|
65
98
|
msg = "<fipa-message ontology=\"#{ontology || @ontology}\" receiver=\"#{receiver}\" act=\"inform\"><content>#{answer}</content></fipa-message>"
|
66
99
|
@im.send!("<message type=\"groupchat\" to=\"#{JABBER_CONFERENCE}\" id=\"aaefa\"><body>#{msg.gsub('&', '&').gsub('<', '<').gsub('>', '>')}</body></message>")
|
@@ -80,82 +113,80 @@ class Cirrocumulus
|
|
80
113
|
msg = "<fipa-message ontology=\"#{ontology || @ontology}\" receiver=\"#{receiver}\" act=\"request\"><content>#{action}</content></fipa-message>"
|
81
114
|
@im.send!("<message type=\"groupchat\" to=\"#{JABBER_CONFERENCE}\" id=\"aaefa\"><body>#{msg.gsub('&', '&').gsub('<', '<').gsub('>', '>')}</body></message>")
|
82
115
|
end
|
116
|
+
=end
|
83
117
|
|
118
|
+
# Main loop. Connects to other agents and processes all incoming messages
|
84
119
|
def run(agent, kb, sniff = false)
|
85
120
|
connect(generate_jid(agent))
|
86
121
|
|
87
122
|
Log4r::Logger['cirrocumulus'].info("entering main loop")
|
88
123
|
agent.restore_state()
|
89
|
-
|
124
|
+
agent.start()
|
125
|
+
|
90
126
|
s = Sexpistol.new
|
127
|
+
should_be_running = true
|
91
128
|
|
92
|
-
|
129
|
+
while should_be_running do
|
93
130
|
kb.collect_knowledge()
|
94
|
-
|
131
|
+
|
95
132
|
begin
|
96
|
-
connect(generate_jid(agent)) if
|
133
|
+
connect(generate_jid(agent)) if !connected?
|
97
134
|
rescue Exception => e
|
98
135
|
puts e.to_s
|
99
136
|
sleep 5
|
100
137
|
end
|
101
|
-
|
102
|
-
next if @im.nil? || !@im.connected?
|
103
138
|
|
139
|
+
next if !connected?
|
140
|
+
|
141
|
+
# process incoming messages from queue
|
104
142
|
@im.received_messages do |message|
|
105
|
-
|
106
|
-
|
107
|
-
begin
|
108
|
-
xml = Hash.from_xml(message.body)['fipa_message']
|
109
|
-
|
110
|
-
ontology = xml['ontology']
|
111
|
-
sender = message.from.resource
|
112
|
-
receiver = xml['receiver']
|
113
|
-
supported_ontology = agent.handles_ontology?(ontology)
|
114
|
-
if (supported_ontology && (receiver == @jid || receiver.blank?)) || receiver == @jid || sniff
|
115
|
-
act = xml['act']
|
116
|
-
content_raw = xml['content']
|
117
|
-
content = s.parse_string(content_raw)
|
118
|
-
msg = Cirrocumulus::Message.new(sender, act, content)
|
119
|
-
msg.receiver = receiver
|
120
|
-
msg.reply_with = xml['reply_with']
|
121
|
-
msg.in_reply_to = xml['in_reply_to']
|
122
|
-
msg.ontology = ontology
|
123
|
-
flatten_message_content(msg)
|
124
|
-
Log4r::Logger['cirrocumulus'].debug(msg.inspect)
|
125
|
-
agent.handle_message(msg, kb)
|
126
|
-
else
|
127
|
-
Log4r::Logger['cirrocumulus'].debug("unhandled ontology %s" % [ontology])
|
128
|
-
end
|
129
|
-
rescue Exception => e
|
130
|
-
puts e.to_s
|
131
|
-
puts e.backtrace
|
132
|
-
end
|
143
|
+
process_incoming_message(agent, kb, message, s, sniff)
|
133
144
|
end
|
134
145
|
|
135
|
-
|
136
|
-
|
146
|
+
# send all queued messages
|
147
|
+
process_send_queue()
|
148
|
+
|
149
|
+
sleep 0.5 # TODO: do we need this stuff?
|
137
150
|
end
|
138
151
|
|
139
152
|
@im.disconnect
|
140
153
|
end
|
141
|
-
|
154
|
+
|
142
155
|
private
|
143
|
-
|
156
|
+
|
157
|
+
# Flatterns incoming message if is bounded into multiple arrays
|
158
|
+
def flatten_message_content(message)
|
159
|
+
if !message.content.is_a?(Array)
|
160
|
+
message.content = [message.content]
|
161
|
+
else
|
162
|
+
while message.content.is_a?(Array) && message.content.size == 1 && message.content.first.is_a?(Array)
|
163
|
+
message.content = message.content.first
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Generates JID for self identification
|
144
169
|
def generate_jid(agent)
|
145
170
|
suffix = agent.default_ontology ? agent.default_ontology.gsub('cirrocumulus-', '') : @suffix
|
146
171
|
if @generate_jid
|
147
|
-
_, hostname = systemu
|
172
|
+
_, hostname = systemu('hostname')
|
148
173
|
hostname.strip!
|
149
174
|
"%s-%s" % [hostname, suffix]
|
150
175
|
else
|
151
176
|
suffix
|
152
177
|
end
|
153
178
|
end
|
154
|
-
|
179
|
+
|
180
|
+
# Checks if agent is connected to Jabber conference
|
181
|
+
def connected?
|
182
|
+
@im && @im.connected?
|
183
|
+
end
|
184
|
+
|
185
|
+
# Connects to Jabber server and joins the conference
|
155
186
|
def connect(jid)
|
156
187
|
@jid = jid
|
157
188
|
Log4r::Logger['cirrocumulus'].info "logging as " + @jid
|
158
|
-
|
189
|
+
|
159
190
|
begin
|
160
191
|
@im.disconnect if @im && @im.connected?
|
161
192
|
full_jid = @jid + "@" + JABBER_SERVER
|
@@ -163,7 +194,7 @@ class Cirrocumulus
|
|
163
194
|
rescue Jabber::ClientAuthenticationFailure => ex
|
164
195
|
Log4r::Logger['cirrocumulus'].warn ex.to_s
|
165
196
|
Log4r::Logger['cirrocumulus'].info "registering new account with default password"
|
166
|
-
|
197
|
+
|
167
198
|
client = Jabber::Client.new(full_jid)
|
168
199
|
client.connect()
|
169
200
|
client.register(JABBER_DEFAULT_PASSWORD) #, {'username' => full_jid, 'password' => JABBER_DEFAULT_PASSWORD})
|
@@ -179,14 +210,62 @@ class Cirrocumulus
|
|
179
210
|
@im.send!("<presence to='#{JABBER_CONFERENCE}/#{@jid}' />")
|
180
211
|
end
|
181
212
|
end
|
182
|
-
|
183
|
-
def
|
184
|
-
if !message.
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
213
|
+
|
214
|
+
def process_incoming_message(agent, kb, message, s, sniff)
|
215
|
+
return if !message.x('jabber:x:delay').nil?
|
216
|
+
|
217
|
+
begin
|
218
|
+
xml = Hash.from_xml(message.body)['fipa_message']
|
219
|
+
|
220
|
+
ontology = xml['ontology']
|
221
|
+
sender = message.from.resource
|
222
|
+
receiver = xml['receiver']
|
223
|
+
supported_ontology = agent.handles_ontology?(ontology)
|
224
|
+
|
225
|
+
if (supported_ontology && (receiver == @jid || receiver.blank?)) || receiver == @jid || sniff
|
226
|
+
content_raw = xml['content']
|
227
|
+
content = s.parse_string(content_raw)
|
228
|
+
msg = Cirrocumulus::Message.new(sender, xml['act'], content)
|
229
|
+
msg.receiver = receiver
|
230
|
+
msg.ontology = ontology
|
231
|
+
msg.reply_with = xml['reply_with']
|
232
|
+
msg.in_reply_to = xml['in_reply_to']
|
233
|
+
msg.conversation_id = xml['conversation_id']
|
234
|
+
|
235
|
+
flatten_message_content(msg)
|
236
|
+
Log4r::Logger['cirrocumulus'].debug(msg.inspect)
|
237
|
+
agent.handle_message(msg)
|
238
|
+
else # ignore message
|
239
|
+
#Log4r::Logger['cirrocumulus'].debug("unhandled ontology: %s" % [ontology])
|
189
240
|
end
|
241
|
+
rescue Exception => e # exception while parsing; possibly it is not XML (humans speaking!)
|
242
|
+
Log4r::Logger['cirrocumulus'].error "%s\n%s" % [e.to_s, e.backtrace.to_s]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Processes send queue and sends all pending messages
|
247
|
+
def process_send_queue()
|
248
|
+
while !@send_queue.empty? do
|
249
|
+
message = @send_queue.pop
|
250
|
+
actual_send(message)
|
190
251
|
end
|
191
252
|
end
|
253
|
+
|
254
|
+
# Sends message to Jabber conference
|
255
|
+
def actual_send(message)
|
256
|
+
msg = "<fipa-message ontology=\"#{message.ontology}\""
|
257
|
+
msg += " receiver=\"#{message.receiver}\"" if message.receiver
|
258
|
+
msg += " act=\"#{message.act}\""
|
259
|
+
msg += " reply-with=\"#{message.reply_with}\"" if message.reply_with
|
260
|
+
msg += " in-reply-to=\"#{message.in_reply_to}\"" if message.in_reply_to
|
261
|
+
msg += " conversation-id=\"#{message.conversation_id}\"" if message.conversation_id
|
262
|
+
message_content = message.content if message.content.is_a?(String)
|
263
|
+
message_content = Sexpistol.new.to_sexp(message.content) if message.content.is_a? Array
|
264
|
+
msg += "><content>#{message_content}</content></fipa-message>"
|
265
|
+
@im.send!("<message type=\"groupchat\" to=\"#{JABBER_CONFERENCE}\" id=\"aaefa\"><body>#{msg.gsub('&', '&').gsub('<', '<').gsub('>', '>')}</body></message>")
|
266
|
+
true
|
267
|
+
rescue
|
268
|
+
false
|
269
|
+
end
|
270
|
+
|
192
271
|
end
|