# An experiment on NITOS, using Backpressure routing over a wireless 4-nodes Mesh.
# Run : omf_ec -u xmpp://nitlab.inf.uth.gr exec --oml_uri tcp:nitlab.inf.uth.gr:3003 click-exp-omf6.rb -- --oml_domain click-26

defProperty('sender_id',  50, "The id of the sender")
defProperty('receiver_id',  51, "The id of the receiver")
defProperty('forwarder1_id',  52, "The id of the forwarder 1")
defProperty('forwarder2_id',  53, "The id of the forwarder 2")

defProperty('ip_net', '5.0.0.', "The ip network prefix")

defProperty('oml_domain', 'click', "The OML domain for this experiment")

defProperty('hostname_prefix', 'node', "The prefix of the hostname of the used facility nodes")
defProperty('hostname_leading_zeros', 3, "The number of the leading zeros before the id, in the hostname of the nodes")

def hostname(node_id)
  suffix = "#{node_id}".rjust("#{property.hostname_leading_zeros}".to_i,'0')
  "#{property.hostname_prefix}#{suffix}"
end

mac = {
  50 => '00:1e:c2:f5:39:25',
  51 => '10:9a:dd:a5:d0:0a',
  52 => '10:9a:dd:a6:2c:e7', 
  53 => 'e4:ce:8f:59:0e:41',
  55 => 'e4:ce:8f:66:70:51',
  57 => 'e4:ce:8f:67:5d:9e',
  59 => 'e4:ce:8f:67:8e:7c',
  60 => 'e4:ce:8f:64:7d:07',
  62 => 'e4:ce:8f:51:28:2c',
  63 => '10:9a:dd:9d:cd:b2',
  64 => '10:9a:dd:9f:c3:bb'
}


defApplication('click-config') do |a| 
  a.binary_path = "/root/click/conf/wifi/bp/gen_config_roofnet_bp.pl"
  a.defProperty('bp', 'bp', '--bp', {:type => :boolean, :dynamic => false, :value => true})
  a.defProperty('enhanced', 'enhanced', '--enhanced', {:type => :boolean, :dynamic => false, :value => true})
  a.defProperty('id', 'id', '--id', {:type => :integer, :dynamic => false})
  a.defProperty('monitor', 'monitor', '--mon', {:type => :boolean, :dynamic => false, :value => true})
  a.defProperty('slot', 'slot', '--slot', {:type => :integer, :dynamic => false, :value => 100})
  a.defProperty('capacity', 'capacity', '--capacity', {:type => :integer, :dynamic => false, :value => 1000})
  a.defProperty('channel', 'channel', '--channel', {:type => :integer, :dynamic => false, :value => 36})
  a.defProperty('neighborhood', 'neighborhood', '--neighborhood', {:type => :string, :dynamic => false})
  a.defProperty('click_file', 'click configuration file', ' > ', {:type => :string, :dynamic => false, :value => '/tmp/tmp.click'})
  a.defProperty('msg_output', 'messages output', '', {:type => :string, :dynamic => false, :value => '2>/dev/null'})
end

defApplication('click') do |a| 
  a.binary_path = "/root/click/userlevel/click"
  a.defProperty('config', 'configuration file', '', {:type => :string, :dynamic => false, :value => '/tmp/tmp.click'})
  a.defProperty('oml_collect', 'OML server collecting the measurements', '--oml-collect', {:type => :string, :dynamic => false, :value => 'tcp:nitlab.inf.uth.gr:3003'})
  a.defProperty('oml_domain', 'OML domain or experiment identifier', '--oml-domain', {:type => :string, :dynamic => false, :value => 'click'})
  a.defProperty('oml_id', 'OML node identifier', '--oml-id', {:type => :string, :dynamic => false})
  a.defProperty('msg_output', 'messages output', ' > ', {:type => :string, :dynamic => false, :value => '/dev/null 2>&1'})
end

defApplication('iperf') do |a| 
  a.binary_path = "/usr/bin/oml2-iperf"
  a.defProperty('port', 'set server port to listen on/connect to to n (default 5001)', '-p', {:type => :integer, :dynamic => false})
  a.defProperty('server', 'run in server mode',	'-s', {:type => :boolean, :dynamic => false})
  a.defProperty('client', 'run in client mode, connecting to host', '-c', {:order => 1, :type => :string, :dynamic => false})
  a.defProperty('udp', 'use UDP rather than TCP', '-u', {:order => 2, :type => :boolean, :dynamic => false})
  a.defProperty('time', 'time in seconds to transmit for (default 10 secs)', '-t', {:type => :integer, :dynamic => false})
  a.defProperty('interval', 'pause n seconds between periodic bandwidth reports', '-i', {:type => :integer, :dynamic => false})
  a.defProperty('bandwidth', 'set target bandwidth to n bits/sec (default 1 Mbit/sec)',	'-b', {:type => :integer, :dynamic => false, :unit => "bps"})
  a.defProperty('length', 'packet length', '-l', {:type => :integer, :dynamic => false, :value => 1470})
  a.defProperty('msg_output', 'messages output', ' > ', {:type => :string, :dynamic => false})
  a.defMeasurement("transfer") do |m|
    m.defMetric('pid', :long, 'Main process identifier')
    m.defMetric('connection_id', :long, 'Connection identifier (socket)')
    m.defMetric('begin_interval', :float, 'Start of the averaging interval (Iperf timestamp)')
    m.defMetric('end_interval', :float, 'End of the averaging interval (Iperf timestamp)')
    m.defMetric('size', :long, 'Amount of transmitted data [Bytes]')
  end
end


defGroup('sender', hostname(property.sender_id)) do |node|
  node.addApplication('click-config') do |app|
    app.name = 'click-config'
    app.setProperty('neighborhood', mac[property.forwarder1_id.to_i]+','+mac[property.forwarder2_id.to_i]);
    app.setProperty('id', 101);
  end
  node.addApplication('click') do |app|
    app.name = 'click'
    app.setProperty('oml_id', 'sender')
    app.setProperty('oml_domain', property.oml_domain)
  end
  node.addApplication("iperf") do |app|
    app.name = 'iperf-sender'
    app.setProperty('port', 5200)
    app.setProperty('client', "#{property.ip_net}100")
    app.setProperty('udp', true)
    app.setProperty('time', 120)
    app.setProperty('bandwidth', 3000000)
    app.setProperty('length', 1300)
    app.setProperty('msg_output', '/dev/null 2>&1')
  end
end

defGroup('receiver', hostname(property.receiver_id)) do |node|
  node.addApplication('click-config') do |app|
    app.name = 'click-config'
    app.setProperty('neighborhood', mac[property.forwarder1_id.to_i]+','+mac[property.forwarder2_id.to_i]);
    app.setProperty('id', 100);
  end
  node.addApplication('click') do |app|
    app.name = 'click'
    app.setProperty('oml_id', 'receiver')
    app.setProperty('oml_domain', property.oml_domain)
  end
  node.addApplication("iperf") do |app|
    app.name = 'iperf-receiver'
    app.setProperty('port', 5200)
    app.setProperty('server', true)
    app.setProperty('udp', true)
    app.setProperty('length', 1300)
    app.setProperty('interval', 1)  
    #app.measure('transfer', :interval => 1)
  end
end

defGroup('forwarder1', hostname(property.forwarder1_id)) do |node|
  node.addApplication('click-config') do |app|
    app.name = 'click-config'
    app.setProperty('neighborhood', mac[property.sender_id.to_i]+','+mac[property.receiver_id.to_i]);
  end
  node.addApplication('click') do |app|
    app.name = 'click'
    app.setProperty('oml_id', 'forwarder1')
    app.setProperty('oml_domain', property.oml_domain)
  end
  node.addApplication("iperf") do |app|
    app.name = 'iperf-forwarder1'
    app.setProperty('port', 5200)
    app.setProperty('client', "#{property.ip_net}101")
    app.setProperty('udp', true)
    app.setProperty('time', 120)
    app.setProperty('bandwidth', 9000000)
    app.setProperty('length', 1300)
    app.setProperty('msg_output', '/dev/null 2>&1')
  end
end

defGroup('forwarder2', hostname(property.forwarder2_id)) do |node|
  node.addApplication('click-config') do |app|
    app.name = 'click-config'
    app.setProperty('neighborhood', mac[property.sender_id.to_i]+','+mac[property.receiver_id.to_i]);
  end
  node.addApplication('click') do |app|
    app.name = 'click'
    app.setProperty('oml_id', 'forwarder2')
    app.setProperty('oml_domain', property.oml_domain)
  end
end


defEvent :CLICK_CONFIG_EXITED do |state|
  click_stopped = 0
  state.each do |resource|
    click_stopped = click_stopped + 1 if (resource.app == 'click-config') && (resource.event == 'EXIT')
  end
  click_stopped == 4
end

defEvent :IPERF_EXITED do |state|
  iperf_stopped = false
  state.each do |resource|
    iperf_stopped = true if (resource.app == 'iperf-sender') && (resource.event == 'EXIT')
  end
  iperf_stopped
end


onEvent :ALL_UP_AND_INSTALLED do
  info "Starting click config"
  group("sender").startApplication("click-config")
  group("receiver").startApplication("click-config")
  group("forwarder1").startApplication("click-config")
  group("forwarder2").startApplication("click-config")
end

onEvent :CLICK_CONFIG_EXITED do
  info "Starting click"
  group("sender").startApplication("click")
  group("receiver").startApplication("click")
  group("forwarder1").startApplication("click")
  group("forwarder2").startApplication("click")
  after 20.seconds do
    info "Starting flow"
    group("receiver").startApplication("iperf-receiver")
    group("sender").startApplication("iperf-sender")
  end
  after 50.seconds do
    group("forwarder1").startApplication("iperf-forwarder1")
  end
  after 80.seconds do
    group("forwarder2").exec("echo 'write data_q.add_flow 5.0.0.100' | nc -q 1 localhost 7780")
  end
end

onEvent :IPERF_EXITED do
  info "Stopping flow"    
  group("sender").stopApplications
  group("receiver").stopApplications
  group("forwarder1").stopApplications
  group("forwarder2").stopApplications
  Experiment.done
end

