Name: simulation/lib/result.rb
| 1: | # Apache License, Version 2.0 |
| 2: | # |
| 3: | # Copyright (c) 2013 Juergen Mangler |
| 4: | # |
| 5: | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6: | # you may not use this file except in compliance with the License. |
| 7: | # You may obtain a copy of the License at |
| 8: | # |
| 9: | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10: | # |
| 11: | # Unless required by applicable law or agreed to in writing, software |
| 12: | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13: | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14: | # See the License for the specific language governing permissions and |
| 15: | # limitations under the License. |
| 16: | |
| 17: | require 'chronic_duration' |
| 18: | |
| 19: | class Result |
| 20: | def self.askForInput(head,doc,label) |
| 21: | ### setting status to asking |
| 22: | meta = JSON.parse(File.read($dir + "/meta")) |
| 23: | meta['status'] = "asking" |
| 24: | File.write($dir + "/meta",JSON.generate(meta)) |
| 25: | |
| 26: | ### generate question.json |
| 27: | str = JSON.pretty_generate(:head => head, :doc => doc, :label => label) |
| 28: | File.write($dir + "/question.json",str) |
| 29: | sleep 2 until File.stat($dir + "/question.json").size > str.length |
| 30: | |
| 31: | ### return result and clean up |
| 32: | result = JSON.parse(File.read($dir + "/question.json"))["result"] |
| 33: | File.unlink($dir + "/question.json") |
| 34: | result |
| 35: | end |
| 36: | |
| 37: | def self.finish |
| 38: | ### setting status to asking |
| 39: | meta = JSON.parse(File.read($dir + "/meta")) |
| 40: | meta['status'] = "finished" |
| 41: | File.write($dir + "/meta",JSON.generate(meta)) |
| 42: | end |
| 43: | |
| 44: | def self.forecast(client,dataelements,endpoints,ep,a,min,avg,max) |
| 45: | return [404, {}] if endpoints[ep].nil? |
| 46: | gw, op = endpoints[ep].split('#',2) |
| 47: | pr, gw = gw.split(/:\/\//) |
| 48: | |
| 49: | doc = XML::Smart.open_unprotected(::File.dirname(__FILE__) + '/../resources/template_req.xml') |
| 50: | doc.register_namespace 'g', 'http://www.fp7-adventure.eu/xmlSchema/Gateways/' |
| 51: | |
| 52: | doc.find("/g:gatewayRequest/g:header/g:operationName").each { |e| e.text = op } |
| 53: | doc.find("/g:gatewayRequest/g:header/g:protocol").each { |e| e.text = pr } |
| 54: | doc.find("/g:gatewayRequest/g:header/g:target").each { |e| e.text = gw } |
| 55: | doc.find("/g:gatewayRequest/g:header/g:start_min").each { |e| e.text = (Time.now + min).xmlschema } |
| 56: | doc.find("/g:gatewayRequest/g:header/g:start_avg").each { |e| e.text = (Time.now + avg).xmlschema } |
| 57: | doc.find("/g:gatewayRequest/g:header/g:start_max").each { |e| e.text = (Time.now + max).xmlschema } |
| 58: | |
| 59: | doc.find("/g:gatewayRequest/g:payload").each do |e| |
| 60: | a.parameters.each do |k,v| |
| 61: | if v[0] == "➤" |
| 62: | vname = v[1..-1] |
| 63: | v = dataelements[vname] |
| 64: | if v.nil? |
| 65: | v = Result::askForInput("Missing Dataelement",File.read(File.dirname(__FILE__) + "/../resources/dataelement.txt"),"Missing \"#{vname}\" for task \"#{a.to_s_nice}\":") |
| 66: | dataelements[vname] = v |
| 67: | end |
| 68: | end |
| 69: | |
| 70: | tmp = JSON.parse(v) rescue v |
| 71: | if tmp.is_a?(Hash) || tmp.is_a?(Array) |
| 72: | e.add("g:inputParameter", XmlSimple.xml_out(tmp,'keeproot'=>''), 'encoding' => 'false', 'paramName' => k) |
| 73: | else |
| 74: | e.add("g:inputParameter", v, 'encoding' => 'false', 'paramName' => k) |
| 75: | end |
| 76: | end |
| 77: | end |
| 78: | |
| 79: | status, result = client.post Riddl::Parameter::Complex.new("simulationRequest","text/xml",doc.to_s) |
| 80: | jsonresult = [] |
| 81: | if status == 200 |
| 82: | doc = XML::Smart::string(result[0].value.read) |
| 83: | doc.register_namespace 'r', 'http://www.fp7-adventure.eu/xmlSchema/Gateways/' |
| 84: | doc.find("/r:gatewayResponse/r:payload/r:outputData[@paramName='summary']").each do |e| |
| 85: | res = {} |
| 86: | |
| 87: | det = { :prob => e.attributes['probability'], :min => ChronicDuration.parse(e.attributes['min_time']), :max => ChronicDuration.parse(e.attributes['max_time']), :avg => ChronicDuration.parse(e.attributes['avg_time'])} |
| 88: | if e.text_only? |
| 89: | sum = XML::Smart::string(e.text) |
| 90: | sum.root.namespaces.add(nil,'http://www.fp7-adventure.eu/xmlSchema/Gateways/') |
| 91: | else |
| 92: | sum = e.children.first.to_doc |
| 93: | end |
| 94: | sum.register_namespace 'r', 'http://www.fp7-adventure.eu/xmlSchema/Gateways/' |
| 95: | sum.find("/r:summary/r:info/r:info").each do |i| |
| 96: | res[i.attributes['name'].downcase] = i.attributes['value'] |
| 97: | res[i.attributes['name'].downcase] = (Float(res[i.attributes['name'].downcase]) rescue res[i.attributes['name'].downcase]) |
| 98: | end |
| 99: | jsonresult << [res, det] |
| 100: | end |
| 101: | end |
| 102: | |
| 103: | [status, jsonresult] |
| 104: | end |
| 105: | |
| 106: | def self.manipulate(dataelements,endpoints,result,a) |
| 107: | return [true, dataelements, endpoints] if a.code.nil? |
| 108: | client = Riddl::Client.new("http://fp7-adventure.eu:9292",nil) |
| 109: | resource = client.resource('/') |
| 110: | |
| 111: | status, res = resource.post [ |
| 112: | Riddl::Parameter::Simple.new("dataelements", JSON::generate(dataelements)), |
| 113: | Riddl::Parameter::Simple.new("endpoints", JSON::generate(endpoints)), |
| 114: | Riddl::Parameter::Simple.new("resultname", "result"), |
| 115: | Riddl::Parameter::Simple.new("resultvalue", JSON::generate(result)), |
| 116: | Riddl::Parameter::Simple.new("code", a.code) |
| 117: | ] |
| 118: | |
| 119: | mval = nil |
| 120: | if status == 200 |
| 121: | id = res[0].value |
| 122: | |
| 123: | resource = client.resource('/' + id) |
| 124: | status, res = resource.get |
| 125: | if status == 200 |
| 126: | mval = JSON::parse(res[0].value) |
| 127: | else |
| 128: | return false |
| 129: | end |
| 130: | else |
| 131: | return false |
| 132: | end |
| 133: | |
| 134: | return [true, mval['dataelements'], mval['endpoints']] |
| 135: | end |
| 136: | end |
| 137: | |
| 138: | class ResultTreeNode |
| 139: | attr_reader :message, :endpoint, :dataelements, :endpoints, :id |
| 140: | |
| 141: | attr_reader :start_min, :start_max, :start_avg |
| 142: | attr_reader :min, :max, :avg, :prob |
| 143: | |
| 144: | def initialize(id,message,endpoint,dataelements,endpoints,start_min,start_avg,start_max,min,avg,max,prob) |
| 145: | @sub = [] |
| 146: | @message = message |
| 147: | @endpoint = endpoint |
| 148: | @dataelements = dataelements |
| 149: | @endpoints = endpoints |
| 150: | @id = id |
| 151: | @start_min = start_min |
| 152: | @start_avg = start_avg |
| 153: | @start_max = start_max |
| 154: | @min = min |
| 155: | @avg = avg |
| 156: | @max = max |
| 157: | @prob = prob.to_f/100 |
| 158: | end |
| 159: | |
| 160: | def leaf? |
| 161: | @sub.empty? |
| 162: | end |
| 163: | |
| 164: | def to_s |
| 165: | rec_to_s self, 2, self.prob, self |
| 166: | end |
| 167: | |
| 168: | def rec_to_s(what,indent,prob,root) |
| 169: | coll = '' |
| 170: | ind = " " * indent |
| 171: | coll << ind << "Task: #{what.id}\n" |
| 172: | coll << ind << "Endpoint(s): #{what.endpoint.split(/, /).uniq.join(', ')}\n" unless what.endpoint.nil? |
| 173: | coll << ind << "Result: #{what.message}\n" |
| 174: | coll << ind << "Dataelements: #{what.dataelements.pretty_inspect.strip.gsub(/\n/,"\n#{ind + ' '}")}\n" |
| 175: | coll << ind << "Start: Min. #{Time.now + what.start_min}, Avg. #{Time.now + what.start_avg}, Max. #{Time.now + what.start_max}\n" |
| 176: | coll << ind << "Duration: Min. #{ChronicDuration.output(what.min)}, Avg. #{ChronicDuration.output(what.avg)}, Max. #{ChronicDuration.output(what.max)}\n" |
| 177: | coll << ind << "Probability: #{"%0.2f" % what.prob}\n\n" |
| 178: | if what.leaf? |
| 179: | coll << ind << "↳ Total Probability: #{"%0.2f" % (prob * what.prob)}\n" |
| 180: | coll << ind << " Total Duration:\n" |
| 181: | coll << ind << " Min.: #{ChronicDuration.output(what.start_min+what.min-root.start_min)}" << "\n" |
| 182: | coll << ind << " Avg.: #{ChronicDuration.output(what.start_avg+what.avg-root.start_avg)}" << "\n" |
| 183: | coll << ind << " Max.: #{ChronicDuration.output(what.start_max+what.max-root.start_max)}" << "\n\n" |
| 184: | end |
| 185: | what.each do |s| |
| 186: | coll << rec_to_s(s,indent+2,prob*what.prob,root) |
| 187: | end |
| 188: | coll |
| 189: | end |
| 190: | |
| 191: | def <<(what) |
| 192: | @sub << what |
| 193: | end |
| 194: | |
| 195: | def each(&what) |
| 196: | @sub.each do |s| |
| 197: | what.call s |
| 198: | end |
| 199: | end |
| 200: | |
| 201: | def each_leaf(&what) |
| 202: | if leaf? |
| 203: | what.call self |
| 204: | else |
| 205: | rec_leaf(self).each do |s| |
| 206: | what.call s |
| 207: | end |
| 208: | end |
| 209: | end |
| 210: | |
| 211: | def rec_leaf(sub) |
| 212: | coll = [] |
| 213: | sub.each do |s| |
| 214: | if s.leaf? |
| 215: | coll << s |
| 216: | else |
| 217: | coll += rec_leaf s |
| 218: | end |
| 219: | end |
| 220: | coll |
| 221: | end |
| 222: | private :rec_leaf |
| 223: | end |
