"""
ant distsim-rt-jpy -Ddriver.class=res/test/SimRemoteHiddenNode.py  -Ddb.definition.url=jdbc:mysql://coxon/simulation -Ddriver.version=cgraph1.40
ant db-init -Ddb.name=evalCGraph1_37
ant distsim-eval-jpy2 -Ddriver.class=res/test/SimRemoteHiddenNode.py -Ddb.result.url=jdbc:mysql://coxon/simulation -Ddb.eval.url=jdbc:mysql://localhost:3306/evalCGraph1_37 -Ddriver.version=cgraph1.37 > out.txt
"""

import sys
#import os

from math import ceil
#import stats
from jarray import array, zeros
from java.lang import *
from java.util import *
from java.io import StringReader
import java.lang.reflect

from jist.swans import Constants
from jist.swans.net import NetAddress
from jist.swans.misc import Util

from brn.sim import *
from brn.sim.data import *
from brn.sim.data.dump import WiresharkDump
from brn.sim.builder import *
from brn.sim.handler import LinkQualityHandler
import brn.swans.Constants as BrnConstants

from click.sim.builder import *
from click.runtime.remote import *

from brn.distsim.ormapper.util import *

true = 1
false = 0

NONE = DataManager.LEVEL_OFF
LESS = DataManager.LEVEL_IMPORTANT
BASIC = DataManager.LEVEL_BASIC
MORE = DataManager.LEVEL_ADDITIONAL
ALL = DataManager.LEVEL_ALL

def createArray1(w,default=None):
  return [default]*w

def createArray2(w,h,default=None):
  return [ [default]*w for i in range(h) ]

def createArray3(a,b,c,default=None):
  return [ [ [default]*a for i in range(b) ] for i in range(c) ]

def arrayToDict1(arr,startIdx=0):
  ret = {}
  for idx in range(0,len(arr)):
    ret[idx+startIdx] = arr[idx]
  return ret

def arrayToDict2(arr,startIdx=0):
  ret = {}
  for idx1 in range(0,len(arr)):
    for idx2 in range(0,len(arr[idx1])):
      ret[(idx1+startIdx,idx2+startIdx)] = arr[idx1][idx2]
  return ret

def arrayToDict3(arr,startIdx=0):
  ret = {}
  for idx1 in range(0,len(arr)):
    for idx2 in range(0,len(arr[idx1])):
      for idx3 in range(0,len(arr[idx1][idx2])):
        ret[(idx1+startIdx,idx2+startIdx,idx3+startIdx)] = arr[idx1][idx2][idx3]
  return ret

class LinkResult(Object):
    pass

class SimulationResult(Object):
    pass

class ResultGraph(Object):
    pass

# theoretic max. PPS@1Mbps: 78.44
# achievable: 78.03 (wgt32), 78.15 (wgt31), 79 (wgt55)
# NOTE: for the nodes in house 4, the OpenVPN connection is the bottleneck
class SimRemoteHiddenNode(AbstractDriver):
    def __init__(self):
      self.startupWgtScript = """export PATH=${PATH}:/usr/sbin
WORKING_DIR=/home/kurth/tmp/mips
insmod wlan
insmod ath_hal
insmod wlan_scan_sta
insmod ath_rate_minstrel
insmod ath_rate_sample
insmod ath_pci  countrycode=395 outdoor=1 # yes, you are Japan
wlanconfig ath0 destroy 2>&1 || true
wlanconfig ath0 create wlandev wifi0 wlanmode monitor
iwconfig ath0 essid WiFiNoDe channel $CHANNEL rts off rate 1M
echo '804' > /proc/sys/net/ath0/dev_type
# ip is bogus, but there must be an ip set
ifconfig ath0 10.8.0.1 netmask 255.255.255.0 mtu 2290 up
ifconfig ath0 txqueuelen 1  
ifconfig wifi0 txqueuelen 1  
sysctl -w dev.ath.calibrate=3600 # calibrate interval 1 hour 
iwpriv ath0 wmm 0       # no multimedia
iwpriv ath0 ar 0        # no adaptive radio
iwpriv ath0 burst 0     # no burst
iwpriv ath0 ff 0        # no fast frame
iwpriv ath0 abolt 0     # no atheros proprietary in general
sysctl -w dev.ath.calibrate=3600 # calibrate interval 1 hour 
sysctl -w dev.wifi0.diversity=0
sysctl -w dev.wifi0.txantenna=1
sysctl -w dev.wifi0.rxantenna=1
sysctl -w dev.wifi0.intmit=0
iwconfig ath0 txpower 20  # set power
sysctl -w net.ath0.monitor_phy_errors=1 # set errors
sysctl -w net.ath0.monitor_crc_errors=1 # set errors
sysctl -w dev.ath.calibrate=3600 # calibrate interval 1 hour 
cd ${WORKING_DIR} && ./click --allow-reconfigure -e "_simDeviceMaster :: SimDeviceMaster(TCP, 7777)" > out-$HOST.txt 2>&1 &
      """
      self.startupWgtScriptNoCca = """MODULE_PATH=/home/kurth/projects/madwifi-noCCA/modules
export PATH=${PATH}:/usr/sbin
WORKING_DIR=/home/kurth/tmp/mips
insmod ${MODULE_PATH}/wlan.ko
insmod ${MODULE_PATH}/ath_hal.ko
insmod ${MODULE_PATH}/wlan_scan_sta.ko
insmod ${MODULE_PATH}/ath_rate_minstrel.ko
insmod ${MODULE_PATH}/ath_rate_sample.ko
insmod ${MODULE_PATH}/ath_pci.ko  countrycode=395 outdoor=1 # yes, you are Japan
wlanconfig ath0 destroy 2>&1 || true
wlanconfig ath0 create wlandev wifi0 wlanmode monitor
iwconfig ath0 essid WiFiNoDe channel $CHANNEL rts off rate 1M
echo '804' > /proc/sys/net/ath0/dev_type
# ip is bogus, but there must be an ip set
ifconfig ath0 10.8.0.1 netmask 255.255.255.0 mtu 2290 up
ifconfig ath0 txqueuelen 1  
ifconfig wifi0 txqueuelen 1  
iwpriv ath0 wmm 0       # no multimedia
iwpriv ath0 ar 0        # no adaptive radio
iwpriv ath0 burst 0     # no burst
iwpriv ath0 ff 0        # no fast frame
iwpriv ath0 abolt 0     # no atheros proprietary in general
sysctl -w dev.wifi0.diversity=0
sysctl -w dev.wifi0.txantenna=1
sysctl -w dev.wifi0.rxantenna=1
sysctl -w dev.wifi0.intmit=0
sysctl -w dev.ath.calibrate=3600 # calibrate interval 1 hour 
iwconfig ath0 txpower 20  # set power
sysctl -w net.ath0.monitor_phy_errors=1 # set errors
sysctl -w net.ath0.monitor_crc_errors=1 # set errors
sysctl -w dev.wifi0.disable_cca=1 # 3 and 7 not working, receiver problems
cd ${WORKING_DIR} && ./click --allow-reconfigure -e "_simDeviceMaster :: SimDeviceMaster(TCP, 7777)" > out-$HOST.txt 2>&1 &
      """
      self.startupWrapScript = """MODULE_PATH=/jens/nodes/lib/modules/i586_seismo
export PATH=${PATH}:/usr/sbin
WORKING_DIR=/home/kurth/tmp
CHANNEL=1
insmod ${MODULE_PATH}/wlan.ko
insmod ${MODULE_PATH}/ath_hal.ko
insmod ${MODULE_PATH}/wlan_scan_sta.ko
insmod ${MODULE_PATH}/ath_rate_minstrel.ko
insmod ${MODULE_PATH}/ath_rate_sample.ko
insmod ${MODULE_PATH}/ath_pci.ko  countrycode=395 outdoor=1 # yes, you are Japan
wlanconfig ath0 destroy 2>&1 || true
wlanconfig ath1 destroy 2>&1 || true
#wlanconfig ath0 create wlandev wifi1 wlanmode monitor # use wifi1 with 11a support
wlanconfig ath0 create wlandev wifi0 wlanmode monitor
iwconfig ath0 essid WiFiNoDe channel ${CHANNEL} rts off rate 1M
echo '804' > /proc/sys/net/ath0/dev_type
iwpriv ath0 mode 11b     # 802.11a
iwpriv ath0 wmm 0       # no multimedia
iwpriv ath0 ar 0        # no adaptive radio
iwpriv ath0 burst 0     # no burst
iwpriv ath0 ff 0        # no fast frame
iwpriv ath0 abolt 0     # no atheros proprietary in general
sysctl -w dev.wifi0.diversity=0  # use fixed antenna
sysctl -w dev.wifi0.txantenna=1
sysctl -w dev.wifi0.rxantenna=1
sysctl -w dev.wifi0.intmit=0     # no ANI
# ip is bogus, but there must be an ip set
ifconfig ath0 10.8.0.1 netmask 255.255.255.0 mtu 2290 up
cd ${WORKING_DIR} && ./click --allow-reconfigure -e "_simDeviceMaster :: SimDeviceMaster(TCP, 7777)" > out-$HOST.txt 2>&1 &
      """
      self.shutdownWgtScript = """export PATH=${PATH}:/usr/sbin
killall click 2>&1 || true
ifconfig ath0 down 2>&1 || true
wlanconfig ath0 destroy 2>&1 || true
rmmod ath_pci 2>&1 || true
rmmod wlan_xauth 2>&1 || true
rmmod wlan_wep 2>&1 || true
rmmod wlan_tkip 2>&1 || true
rmmod wlan_ccmp 2>&1 || true
rmmod wlan_acl 2>&1 || true
rmmod ath_rate_minstrel 2>&1 || true
rmmod ath_rate_sample 2>&1 || true
rmmod ath_hal 2>&1 || true
rmmod wlan_scan_sta 2>&1 || true
rmmod wlan_scan_ap 2>&1 || true
rmmod wlan 2>&1 || true
killall click 2>&1 || true
      """
      # TODO tx feedback
      self.clickScriptLinkProbe = """
_simDeviceMaster :: SimDeviceMaster(TCP, 7777, HOTSWAP true, SIMINTERFACES);

AddressInfo(my_wlan ath0:eth);
winfo :: WirelessInfo();

FromSimDevice(local, 8192)
  -> EtherEncap(0x0800, my_wlan, FF:FF:FF:FF:FF:FF) //src dst
  -> WifiEncap(0, 00:00:00:00:00:00)
  -> SetTXRate(RATE 2)
  -> Queue()
  -> tee :: PullTee()
  -> AthdescEncap()
  -> ToDevice(ath0);

FromDevice(ath0, SNAPLEN 4096, PROMISC true)
  -> AthdescDecap()
  //-> filter_phy :: FilterPhyErr()
  -> filter :: FilterTX()
  -> WifiDupeFilter()
  -> host_filter :: HostWifiFilter(my_wlan, winfo, MODE 2)
  -> ExtraEncap()
  -> to_sim_device :: ToSimDevice(local, ENCAP WIFI_EXTRA);

tee[1] // feed back sent packets
  -> ExtraEncap() -> Discard();
  //-> to_sim_device;
      """

      # TODO use ToIPSummaryDump to reduce feedback bandwidth for 5X nodes in house 4
      self.clickScriptIpSummary = """
_simDeviceMaster :: SimDeviceMaster(TCP, 7777, HOTSWAP true, SIMINTERFACES);

AddressInfo(my_wlan ath0:eth);
winfo :: WirelessInfo();
dump :: ToIPSummaryDump($DUMPPREFIX$NODEID.txt, VERBOSE true, CAREFUL_TRUNC false,
  CONTENTS timestamp ip_src ip_id sport dport);

FromSimDevice(local, 8192)
  -> Queue()
  -> SetTimestamp(FIRST false)
  -> CheckIPHeader2()
  -> tee :: PullTee()
  -> EtherEncap(0x0800, my_wlan, FF:FF:FF:FF:FF:FF) //src dst
  -> WifiEncap(0, 00:00:00:00:00:00)
  -> SetTXRate(RATE 2)
  -> AthdescEncap()
  -> ToDevice(ath0);
tee[1]
  -> dump;

FromDevice(ath0, SNAPLEN 4096, PROMISC true)
  -> AthdescDecap()
  //-> filter_phy :: FilterPhyErr()
  -> filter :: FilterTX()
  -> WifiDupeFilter()
  -> Classifier (0/08%0c)
  -> WifiDecap()
  -> HostEtherFilter(my_wlan, DROP_OTHER true, DROP_OWN false)
  -> Strip(14)
  -> CheckIPHeader2()
  -> IPClassifier(src net 0.0.0.0/16)
  -> CopyRXStats(20) // rate signal noise
  -> dump;
      """
      self.clickScriptIpSummaryAligned = """
_simDeviceMaster :: SimDeviceMaster(TCP, 7777, HOTSWAP true, SIMINTERFACES);
AddressInfo@2 :: AddressInfo(my_wlan ath0:eth);
winfo :: WirelessInfo;
dump :: ToIPSummaryDump($DUMPPREFIX$NODEID.txt, VERBOSE true, CAREFUL_TRUNC false,
  CONTENTS timestamp ip_src ip_id sport dport);
FromSimDevice@5 :: FromSimDevice(local, 8192);
Queue@6 :: Queue;
tee :: PullTee;
CheckIPHeader2@8 :: CheckIPHeader2;
EtherEncap@9 :: EtherEncap(0x0800, my_wlan, FF:FF:FF:FF:FF:FF);
WifiEncap@10 :: WifiEncap(0, 00:00:00:00:00:00);
SetTXRate@11 :: SetTXRate(RATE 2);
AthdescEncap@12 :: AthdescEncap;
ToDevice@13 :: ToDevice(ath0);
FromDevice@14 :: FromDevice(ath0, SNAPLEN 4096, PROMISC true);
AthdescDecap@15 :: AthdescDecap;
//filter_phy :: FilterPhyErr;
filter :: FilterTX;
WifiDupeFilter@18 :: WifiDupeFilter;
Classifier@19 :: Classifier(0/08%0c);
WifiDecap@20 :: WifiDecap;
HostEtherFilter@21 :: HostEtherFilter(my_wlan, DROP_OTHER true, DROP_OWN false);
Strip@22 :: Strip(14);
CheckIPHeader2@23 :: CheckIPHeader2;
IPClassifier@24 :: IPClassifier(src net 0.0.0.0/16);
Align@click_align@25 :: Align(4, 0);
setTimestamp :: SetTimestamp(FIRST false);
CopyRXStats@25 :: CopyRXStats(20);
AlignmentInfo@click_align@26 :: AlignmentInfo(_simDeviceMaster,
  setTimestamp,
  AddressInfo@2,
  winfo,
  dump  4 0,
  FromSimDevice@5,
  Queue@6  4 0,
  tee  4 0,
  CheckIPHeader2@8  4 0,
  EtherEncap@9  4 0,
  WifiEncap@10  4 2,
  SetTXRate@11  4 2,
  AthdescEncap@12  4 2,
  ToDevice@13  4 2,
  FromDevice@14,
  AthdescDecap@15  4 2,
//  filter_phy  4 2,
  filter  4 2,
  WifiDupeFilter@18  4 2,
  CopyRXStats@25  4 0,
  Classifier@19  4 2,
  WifiDecap@20  4 2,
  HostEtherFilter@21  4 2,
  Strip@22  4 2,
  CheckIPHeader2@23  4 0,
  IPClassifier@24  4 0,
  Align@click_align@25  1 0);
FromSimDevice@5 -> Align@click_align@25
    -> Queue@6
    -> setTimestamp
    -> CheckIPHeader2@8
    -> tee
    -> EtherEncap@9
    -> WifiEncap@10
    -> SetTXRate@11
    -> AthdescEncap@12
    -> ToDevice@13;
tee [1] -> dump;
FromDevice@14 -> AthdescDecap@15
    //-> filter_phy
    -> filter
    -> WifiDupeFilter@18
    -> Classifier@19
    -> WifiDecap@20
    -> HostEtherFilter@21
    -> Strip@22
    -> CheckIPHeader2@23
    -> IPClassifier@24
    -> CopyRXStats@25
    -> dump;
      """

    #---------------------------------------------------------------------------
    # Setup Parameter
    #---------------------------------------------------------------------------

    def envParams(self):
        params = AbstractParams()

        # general setup
        params.assertion = true
        params.endTime = 60*1
        params.seed = 1
        params.node = None

        return params

    def getClickNode(self,nodeParams,runid,hosts,clickScript,startupScript,shutdownScript,dumpFilePrefix="",channel=14):
      nodeParams.radio = None
      nodeParams.mac = None
      nodeParams.route = None

      nodeParams.net = ClickBuilder.NetRemoteParams()
      nodeParams.net.hosts = BrnTestbedNodes.getNodeNames(hosts)
      nodeParams.net.nodePositions = BrnTestbedNodes.getNodePositions(hosts)
      nodeParams.net.clickScript = clickScript
      nodeParams.net.startupScript = startupScript
      nodeParams.net.shutdownScript = shutdownScript
      nodeParams.net.dumpFilePrefix = dumpFilePrefix + "dump-run-"+str(runid)+"-node-"
      nodeParams.net.scriptVariables.put("$DUMPPREFIX",nodeParams.net.dumpFilePrefix);
      nodeParams.net.scriptVariables.put("$CHANNEL",str(channel));
      nodeParams.net.pullRemotePackets = true
      nodeParams.net.processRemotePackets = true
      nodeParams.net.collectRemotePacketsAtEnd = false
      nodeParams.net.clickPacketEncap = WiresharkDump.FAKE_DLT_IEEE802_11
      nodeParams.net.connectorProperties = ClickBuilder.CONNECTOR_WGT
      nodeParams.net.protocolMapper = array([ \
            Constants.NET_PROTOCOL_LINK_PROBE, \
            Constants.NET_PROTOCOL_UDP, \
            Constants.NET_PROTOCOL_TCP, \
          ], 'i')

      return nodeParams


    def linkProbeParams(self, runid, rate, sender, receiver):
      params = self.envParams()

      # build sender
      clickScript = self.clickScriptLinkProbe
      startup = self.startupWgtScript
      shutdown = self.shutdownWgtScript
      params.nodeNumber = array( [len(sender), len(receiver)], 'i')
      params.nodeParams = array( [ \
        self.getClickNode(NodeBuilder.ExtensibleParams(),runid,sender,clickScript,startup,shutdown), \
        self.getClickNode(NodeBuilder.ExtensibleParams(),runid,receiver,clickScript,startup,shutdown)], \
        NodeBuilder.NodeParams)

      # metric
      params.nodeParams[0].extension = array([MetricBuilder.EtxParams()], Builder.Params)
      params.nodeParams[0].extension[0].probes = array([ Constants.BANDWIDTH_1Mbps, 1500 ], 'i')
      params.nodeParams[0].extension[0].period = (int)(1000. / rate)
      params.nodeParams[0].extension[0].tau = 1000
      params.nodeParams[0].extension[0].autoStart = true

      params.nodeParams[1].extension = array([MetricBuilder.EtxParams()], Builder.Params)
      params.nodeParams[1].extension[0].probes = array([ Constants.BANDWIDTH_1Mbps, 1500 ], 'i')
      params.nodeParams[1].extension[0].period = 1000 * 60 * 60
      params.nodeParams[1].extension[0].tau = 1000 * 60 * 60
      params.nodeParams[1].extension[0].autoStart = true

      return params


    def cbrParams(self, runid, rate, nodes, dumpFilePrefix="", channel=14, startup=None):
      if None == startup:
        startup = self.startupWgtScript
      params = self.envParams()
      params.endTime = 35

      # build nodes
      #clickScript = self.clickScriptLinkProbe
      clickScript = self.clickScriptIpSummaryAligned
      shutdown = self.shutdownWgtScript
      params.nodeNumber = array( [len(nodes)], 'i')
      params.nodeParams = array( [ self.getClickNode(NodeBuilder.FlowParams(),\
        runid,nodes,clickScript,startup,shutdown,dumpFilePrefix,channel)], NodeBuilder.NodeParams)
      params.nodeParams[0].net.pullRemotePackets = false
      params.nodeParams[0].net.processRemotePackets = false

      params.nodeParams[0].trans = TransBuilder.UdpParams()

      flows = []
      for j in range(1,params.nodeNumber[0]+1):
        flow = FlowBuilder.CbrUdpParams()
        flow.flowId = j
        flow.start = 1000 * 2
        flow.end   = 1000 * 32
        flow.udpTxRate = rate[j-1]
        flow.poissonArrival = True
        flow.clientAddr = NetAddress(j)
        flow.serverAddr = NetAddress.ANY
        flow.clientPort = 2816
        flow.serverPort = 2816
        flows += [flow]
      params.nodeParams[0].flows = array(flows, Builder.Params)

      return params

    def cbrParams2(self, runid, rate, nodes, dumpFilePrefix="", channel=14):
      sender = []
      senderRate = []
      receiver = []
      receiverRate = []
      for i in range(0,len(nodes)):
        if rate[i] > 1:
          sender += [nodes[i]]
          senderRate += [rate[i]]
        else:
          receiver += [nodes[i]]
          receiverRate += [rate[i]]
      
      params = self.cbrParams(runid, receiverRate, receiver, dumpFilePrefix, \
          channel, self.startupWgtScript)
      
      # generate new tx params
      startup = self.startupWgtScriptNoCca
      shutdown = params.nodeParams[0].net.shutdownScript
      clickScript = params.nodeParams[0].net.clickScript
      txParams = self.getClickNode(NodeBuilder.FlowParams(),runid,sender,\
          clickScript,startup,shutdown,dumpFilePrefix,channel)
      txParams.net.pullRemotePackets = false
      txParams.net.processRemotePackets = false
      txParams.trans = TransBuilder.UdpParams()

      params.nodeNumber = array( [params.nodeNumber[0], len(sender)], 'i')
      params.nodeParams = array( [params.nodeParams[0], txParams], NodeBuilder.NodeParams)

      flows = []
      for j in range(1,params.nodeNumber[1]+1):
        flow = FlowBuilder.CbrUdpParams()
        flow.flowId = params.nodeNumber[0]+j
        flow.start = 1000 * 2
        flow.end   = 1000 * 32
        flow.udpTxRate = senderRate[j-1]
        flow.poissonArrival = True
        flow.clientAddr = NetAddress(flow.flowId)
        flow.serverAddr = NetAddress.ANY
        flow.clientPort = 2816
        flow.serverPort = 2816
        flows += [flow]
      params.nodeParams[1].flows = array(flows, Builder.Params)

      return params

    # TODO pull received (and transmitted?) packets from all nodes
    def dsrCbrParams(self, runid, nodes, src, dst, rate):
      params = self.envParams()
      params.endTime = 120

      # build nodes
      clickScript = "res/click/r_main_aligned.click"
      startup = self.startupWgtScript
      shutdown = self.shutdownWgtScript
      params.nodeNumber = array( [len(nodes)], 'i')
      params.nodeParams = array( [ self.getClickNode(NodeBuilder.FlowParams(),\
        runid,nodes,clickScript,startup,shutdown)], NodeBuilder.NodeParams)
      params.nodeParams[0].net.clickPacketEncap = WiresharkDump.FAKE_DLT_EN10MB

      flows = []
      for i in range(0,len(src)):
        flow = FlowBuilder.CbrUdpParams()
        flow.flowId = i
        flow.start = 1000 * 30
        flow.end   = 1000 * 100
        flow.udpTxRate = rate[i]
        flow.poissonArrival = True
        flow.clientAddr = NetAddress(src[i])
        flow.serverAddr = NetAddress(dst[i])
        flows += [flow]
      params.nodeParams[0].flows = array(flows, Builder.Params)

      return params

    # TODO switch corrupted packets on
    def netcodingCbrParams(self, runid, nodes, src, dst, rate):
      params = self.envParams()
      params.endTime = 90

      # build nodes
      clickScript = "res/click/r_netcoding_main.click" #"res/click/r_netcoding_main_aligned.click"
      startup = self.startupWrapScript #self.startupWgtScript
      shutdown = self.shutdownWgtScript
      params.nodeNumber = array( [len(nodes)], 'i')
      params.nodeParams = array( [ self.getClickNode(NodeBuilder.FlowParams(),\
        runid,nodes,clickScript,startup,shutdown)], NodeBuilder.NodeParams)

      params.nodeParams[0].net.clickPacketEncap = WiresharkDump.FAKE_DLT_EN10MB
      params.nodeParams[0].net.connectorProperties = ClickBuilder.CONNECTOR_WRAP

      flows = []
      for i in range(0,len(src)):
        flow = FlowBuilder.CbrUdpParams()
        flow.flowId = i
        flow.start = 1000 * 60
        flow.end   = 1000 * 85
        flow.udpTxRate = rate[i]
        flow.poissonArrival = True
        flow.clientAddr = NetAddress(src[i])
        flow.serverAddr = NetAddress(dst[i])
        flows += [flow]
      params.nodeParams[0].flows = array(flows, Builder.Params)

      return params

    #---------------------------------------------------------------------------
    # Evaluation
    #---------------------------------------------------------------------------

    def getSimulationSuite(self, version):
      if ("cgraph1.66" == version):
        return self.getSimulationSuiteCGraph1(version,14)
      if ("cgraph2.9" == version):
        return self.getSimulationSuiteCGraph2(version,14)
      raise Exception("No simulation suite for version " + str(version))

    def evalSimulationResults(self, dbUrl, dbUser, dbPasswd, driver, version, \
                              dbResUrl, dbResUser, dbResPasswd):
      driver = driver.replace('\\', '/')
      if ("cgraph1.63" == version):
        self.evalCGraph1(dbUrl, dbUser, dbPasswd, driver, version, \
            dbResUrl, dbResUser, dbResPasswd)
        return
      if ("cgraph2.5" == version):
        self.evalCGraph1(dbUrl, dbUser, dbPasswd, driver, version, \
            dbResUrl, dbResUser, dbResPasswd)
        return
      raise Exception("No evaluator for version " + str(version))

    # TODO atheros xr mode (low OFDM bit-rates, prob: only in STA mode supported)
    #
    # determine conflict graphs
    def getSimulationSuiteCGraph1(self,version,channel):
      self.setRandomizeSimulationSuite(False);
      list = ArrayList()

      nodes = [81, 51, 52, 53, 54, 55, 63, 49, 41, 25, 33, 31, 79, 80]
      runid = 0
      maxrate = 80
      dumpFilePrefix = "/home/kurth/results/"+version+"/"

      # first, single sender
      for idx in range(0,len(nodes)):
        runid = runid+1
        rates = [maxrate * .0000001] * len(nodes)
        rates[idx] = maxrate
        params = self.setSimParams(self.cbrParams(runid,rates,nodes,dumpFilePrefix,channel))
        list.add(self.setSimParams(params))

      # second, all sender pairs
      for idx1 in range(0,len(nodes)):
        for idx2 in range(idx1+1,len(nodes)):
          runid = runid+1
          rates = [maxrate * .0000001] * len(nodes)
          rates[idx1] = maxrate
          rates[idx2] = maxrate
          params = self.setSimParams(self.cbrParams(runid,rates,nodes,dumpFilePrefix,channel))
          list.add(self.setSimParams(params))

      return list

    def evalCGraph1(self, dbUrl, dbUser, dbPasswd, driver, version, \
                 dbResUrl, dbResUser, dbResPasswd):
      loader = DbBinaryLoader(dbUrl, dbUser, dbPasswd)
      lstIds = loader.loadSimulationIds(driver, version)
      saver = DBSaver(dbResUrl, dbResUser, dbResPasswd, dbResUrl, dbResUser, dbResPasswd)
      saverBinary = DbBinarySaver(dbResUrl, dbResUser, dbResPasswd, dbResUrl, dbResUser, dbResPasswd)
      self.saveBinary = []
      if (None == lstIds or lstIds.size() <= 0):
        raise Exception("no simulations found for driver " + driver + \
            " and version " + version)

      links = {}
      self.snrDB = {}
      for simulationId in lstIds:
        print "Load simulation " + str(simulationId) + " from database"
        result = SimulationResult()
        result.simid = simulationId
        result.inputRates = {}
        result.txRates = {}
        result.duration = {}
        result.startTime = {}
        result.endTime = {}
        result.packetsTx = {}
        result.packetsRx = {}
        result.rssiRx = {}
        result.sender = []

        params = self.load(loader, simulationId, "Global, Config")
        types = len(params.nodeNumber)
        hosts = {-1:"none"}
        for type in range(0,types):
          for idx in range(0,params.nodeNumber[type]):
            hosts[len(hosts)] = params.nodeParams[type].net.hosts[idx]
        nodesAll = len(hosts) - 1

        for type in range(0,types):
          nodes = params.nodeNumber[type]
          prefix = params.nodeParams[type].net.dumpFilePrefix
          s = prefix.split('/')
          prefix = s[len(s)-1]
          
          for idx in range(0,nodes):
            sender = params.nodeParams[type].flows[idx].clientAddr.getId()
            result.inputRates[sender] = params.nodeParams[type].flows[idx].udpTxRate
            result.duration[sender] = params.nodeParams[type].flows[idx].end - \
                params.nodeParams[type].flows[idx].start
            result.duration[sender] = float(result.duration[sender]) / 1000.
            fileName = prefix + str(sender) + ".txt"
  
            infile = open(fileName, "r")
            for line in infile.readlines():
              line = line.strip()
              if line.startswith("!") or len(line) <= 0:
                continue
              line = line.split(' ')
              if len(line) < 2:
                print fileName, line
              ip = line[1]
              if ip == "-":
                print line
                continue
              id = ip.split('.')
              id = int(id[len(id)-1])
              if id > nodesAll or id < 1:
                continue
              time = float(line[0])
              ipId = int(line[2])
              if id == sender:
                result.packetsTx[sender] = result.packetsTx.get(sender,0.) + 1.
                result.startTime[sender] = min(time, result.startTime.get(sender,time))
                result.endTime[sender] = max(time, result.endTime.get(sender,time))
                #self.packetTransmitted(hosts[sender],time,ipId)
              else:
                result.packetsRx[(id,sender)] = result.packetsRx.get((id,sender),0.) + 1.
                try: # possible packet truncation
                  rssi = int(line[3]) - 512 # assume 1 Mbps (==2<<8)
                  if rssi >= 256 or rssi < 0:
                    print "Strange RSSI value: ", line
                    continue
                  if rssi > 128: rssi = rssi - 256 # snr is signed
                  result.rssiRx[(id,sender)] = result.rssiRx.get((id,sender),[]) + [rssi]
                  self.packetReceived(hosts[id],hosts[sender],time,ipId,rssi,simulationId)
                except ValueError:
                  if line[3] != "-": print "Unknown RSSI: ", line[3]
            # end for readlines
            if result.packetsTx[sender] > 10:
              result.sender.append(sender)
              result.txRates[sender] = .0
              result.duration[sender] = max(result.duration[sender], \
                  result.endTime[sender]-result.startTime[sender])
              if result.duration[sender] > 0:
                result.txRates[sender] = result.packetsTx[sender]/result.duration[sender]
          #end for range
        #end for range type
         
        # build link
        for idx in range(0,len(result.sender)):
          src = result.sender[idx]
          for (xxx,dst) in result.packetsRx.keys():
            if xxx != src:
              continue
            link = LinkResult()
            link.simid = simulationId
            link.prefix = prefix
            link.src = src
            link.srcNode = hosts[src]
            link.srcLoc = BrnTestbedNodes.getNodeRoom(link.srcNode)
            link.dst = dst
            link.dstNode = hosts[dst]
            link.dstLoc = BrnTestbedNodes.getNodeRoom(link.dstNode)
            link.intf = -1 # no interferer
            if len(result.sender)>1:
              link.intf = result.sender[(idx+1)%2]
            link.intfNode = hosts[link.intf]
            link.intfLoc = BrnTestbedNodes.getNodeRoom(link.intfNode)
            # 1 index: tx
            link.packetsTx = float(result.packetsTx[src])
            if link.packetsTx < 10:
              print "hu?"
              continue
            link.duration = result.duration[src]
            link.rateIn = result.inputRates[src]
            link.rateTx = result.txRates[src]
            link.rateTxNorm = link.rateTx / link.rateIn 
            # 2 indices: tx, rx
            link.packetsRx = float(result.packetsRx[(src,dst)])
            link.rateRx = link.packetsRx / link.duration
            link.rateRxNorm = link.rateRx / link.rateIn 
            link.pdr = link.packetsRx / link.packetsTx
            link.rssiAvg = -10
            link.rssiStd = -10
            rssi = result.rssiRx.get((src,dst),[])
            if len(rssi) > 0:
              link.rssiAvg = stats.mean(rssi)
              link.rssiStd = stats.samplestdev(rssi)
            link.pdrDiff = float(-2.)
            link.conflictTx = float(-1.)
            link.conflictRx = float(-1.)
            links[(link.srcNode, link.dstNode, link.intfNode)] = link
        # end for idx
      # end for simulationId 
      
      for link in links.values():
        if link.intf < 0:
          continue
        noInfLink = links.get((link.srcNode, link.dstNode, "none"))
        if None == noInfLink:
          print "Warn: intf-free link not found for ", link.srcNode, link.dstNode, "none"
          continue 
        # use rate to account for duration differences
        link.conflictTx = 1. - link.rateTx / noInfLink.rateTx
        # use pdr to account for differences int #tx packets
        link.conflictRx = 1. - link.pdr / noInfLink.pdr
        link.pdrDiff = noInfLink.pdr - link.pdr

      # generate hostname-idx mapping for arrays
      mapHost2Idx = {}
      for link in links.values():
        src = self.getHostIdx(mapHost2Idx, link.srcNode);
        dst = self.getHostIdx(mapHost2Idx, link.dstNode);
        intf = self.getHostIdx(mapHost2Idx, link.intfNode);
      nodes = len(mapHost2Idx) - 1 # exclude none
      
      # create matlab structures
      txP = createArray1(nodes,.0)
      rxP = createArray2(nodes,nodes,.0)
      rxRssi = createArray2(nodes,nodes,.0)
      pdr = createArray2(nodes,nodes,.0)
      txP2 = createArray2(nodes,nodes,.0) # how much did idx1 send, if idx2 was active
      cTx  = createArray2(nodes,nodes,.0) # to which fraction reduces idx1 if idx2 was active
      rxP2 = createArray3(nodes,nodes,nodes,.0) # how much p. were delivered on (i1,i2), if i3 was active
      pdr2 = createArray3(nodes,nodes,nodes,.0)
      rxRssi2 = createArray3(nodes,nodes,nodes,.0) 
      cRx = createArray3(nodes,nodes,nodes,.0)

      for link in links.values():
        src = mapHost2Idx[link.srcNode]
        dst = mapHost2Idx[link.dstNode]
        intf = mapHost2Idx[link.intfNode]
        if link.intf < 0:
          txP[src] = link.packetsTx
          rxP[src][dst] = link.packetsRx
          pdr[src][dst] = link.pdr
          rxRssi[src][dst] = link.rssiAvg
        else:
          txP2[src][intf] = link.packetsTx
          cTx[src][intf] = link.conflictTx
          rxP2[src][dst][intf] = link.packetsRx
          pdr2[src][dst][intf] = link.pdr
          rxRssi2[src][dst][intf] = link.rssiAvg
          cRx[src][dst][intf] = link.conflictRx

      print "mapHost2Idx=", mapHost2Idx
      print "txP="
      print txP
      print "rxP="
      print rxP
      print "pdr="
      print pdr
      print "rxRssi="
      print rxRssi
      print "txP2="
      print txP2
      print "cTx="
      print cTx
      print "rxP2="
      print rxP2
      print "pdr2="
      print pdr2
      print "rxRssi2="
      print rxRssi2
      print "cRx="
      print cRx
      
      # and out
      result = ResultGraph()
      result.driver = driver
      result.version = version
      result.links = links.values()
      saver.save(result, driver + "-" + version, false)
      id = 1
      for data in self.saveBinary:
        path = data.getPath()[0]
        for i in range(1,len(data.getPath())):
          path = path + ", " + data.getPath()[i];
        saverBinary.save(id, data.getType(), path, data)
        id = id + 1

    def getHostIdx(self, mapHost2Idx, node):
      idx = mapHost2Idx.get(node, -2)
      if -2 == idx:
        if "none" == node:
          mapHost2Idx[node] = -1
        else:
          mapHost2Idx[node] = len(mapHost2Idx)
        idx = mapHost2Idx[node]
      return idx

    def packetTransmitted(self,sender,time,ipId):
      pass # TODO
    
    def packetReceived(self,tx,rx,time,id,snr,simulationId):
      snrDB = self.snrDB.get((tx,rx),None)
      if None == snrDB:
        path = ["All runs", "Node "+str(rx), "SNR link "+str(tx)+" "+str(rx)]
        snrDB = DiagramData(path, "time", "sec", "SNR", "dBm", AbstractDiagramData.MARK)
        snrDB.setActive(True)
        self.snrDB[(tx,rx)] = snrDB
        self.saveBinary = self.saveBinary + [snrDB]
      snrDB.addNextPoint(time, snr)

      snrDB = self.snrDB.get((simulationId,tx,rx),None)
      if None == snrDB:
        path = ["Run "+str(simulationId), "Node "+str(rx), "SNR link "+str(tx)+" "+str(rx)]
        snrDB = DiagramData(path, "time", "sec", "SNR", "dBm", AbstractDiagramData.MARK)
        snrDB.setActive(True)
        self.snrDB[(simulationId,tx,rx)] = snrDB
        self.saveBinary = self.saveBinary + [snrDB]
      snrDB.addNextPoint(time, snr)
      
    
    def getSimulationSuiteCGraph2(self,version,channel):
      self.setRandomizeSimulationSuite(False);
      list = ArrayList()

      nodes = [81, 51, 52, 53, 54, 55, 63, 49, 41, 25, 33, 31, 79, 80]
      runid = 0
      maxrate = 84
      dumpFilePrefix = "/home/kurth/results/"+version+"/"

      # first, single sender
      for idx in range(0,len(nodes)):
        runid = runid+1
        rates = [maxrate * .0000001] * len(nodes)
        rates[idx] = maxrate
        params = self.setSimParams(self.cbrParams2(runid,rates,nodes,dumpFilePrefix,channel))
        list.add(self.setSimParams(params))

      # second, all sender pairs
      for idx1 in range(0,len(nodes)):
        for idx2 in range(idx1+1,len(nodes)):
          runid = runid+1
          rates = [maxrate * .0000001] * len(nodes)
          rates[idx1] = maxrate
          rates[idx2] = maxrate
          params = self.setSimParams(self.cbrParams2(runid,rates,nodes,dumpFilePrefix,channel))
          list.add(self.setSimParams(params))

      return list


    def load(self, loader, simulationId, configPath, error=true):
        ret = loader.load(simulationId, configPath)
        if (None == ret and error):
            raise Exception("Object with path \"" + configPath + \
                            "\" not found for id " + str(simulationId))
        return ret

    def evalSimulation(self, dbUrl, dbUser, dbPasswd, driver, version, \
                 dbResUrl, dbResUser, dbResPasswd, callback):
      # get the simulation ids
      loader = DbBinaryLoader(dbUrl, dbUser, dbPasswd)
      lstIds = loader.loadSimulationIds(driver, version)
      saver = DBSaver(dbResUrl, dbResUser, dbResPasswd, dbResUrl, dbResUser, dbResPasswd)
      #results = LinkedList()
      if (None == lstIds or lstIds.size() <= 0):
        raise Exception("no simulations found for driver " + driver + \
            " and version " + version)

      for simulationId in lstIds:
          timeStartLoad = System.currentTimeMillis()
          print "Load simulation " + str(simulationId) + " from database"

          try:
            result = callback(driver, version, simulationId, loader)

            timeEndLoad = System.currentTimeMillis()
            if None != result:
              saver.save(result, driver + "-" + version, false)

            timeNow = System.currentTimeMillis()
            print "   loading : " + str(timeEndLoad-timeStartLoad) + " ms, storing: " +str(timeNow-timeEndLoad)+" ms"

            if simulationId % 25 == 0:
              print "rebuilding session"
              saver.rebuildSession()
          except Exception, inst:
              print "Skip simulation " + str(simulationId) + " (" + inst.getMessage() + ")"
              #inst.printStackTrace()
              continue
          #except:
          #    print "Unexpected error:", sys.exc_info()[0]
          #    continue

#      print "Writing #" + str(len(results)) + " results"
#      for result in results:
#          print "Writing result " + str(result.simid) + " to database"
#          saver.save(result, driver + "-" + version, false)
      saver.close()

    # mk:3160 txpower 20
    # mk:3618 txpower 20
    # mk:3619 txpower 10
    # mk:3620 txpower 1
    # mk:3623,3624 txpower 20
    def runLocalLinkProbe(self):
      rate = 4 # pps
      sender = [81, 51, 52, 53, 54, 55, 63, 49, 41, 25, 33, 31, 79, 80]
      runid = 1
      params = self.setSimParams(self.linkProbeParams(runid, rate, sender, []))
      params.db = true
      self.run(params)

    def runLocalCbr(self):
      nodes = [33, 25, 79, 80, 81] #[81, 51, 52, 53, 54, 55, 63, 49, 41, 25, 33, 31, 79, 80]
      rates = [80 * .0000001] * len(nodes)
      rates[1] = 80
      rates[2] = 80
      runid = 99999
      dumpFilePrefix = "/home/kurth/tmp/mips/"
      params = self.setSimParams(self.cbrParams2(runid, rates, nodes, dumpFilePrefix, \
          channel=14))
      params.db = true
      self.run(params)

    def runLocalDsrCbr(self):
      nodes = [81, 51, 52, 53, 54, 55, 63, 49, 41, 25, 33, 32, 31]
      src = [12]
      dst = [5]
      rate = [80 * .1] * len(src)
      runid = 1
      params = self.setSimParams(self.dsrCbrParams(runid, nodes, src, dst, rate))
      params.handlerLinkQuality = NONE
      params.db = true
      self.run(params)

    def runLocalNetcodingCbr(self):
      nodes = [218, 234]#[81, 51, 52, 53, 54, 55, 63, 49, 41, 25, 33, 32, 31]
      src = [1] #[12]
      dst = [2] #[5]
      rate = [80 * .2] * len(src)
      runid = 1
      params = self.setSimParams(self.netcodingCbrParams(runid, nodes, src, dst, rate))
      params.handlerLinkQuality = NONE
      params.db = true
      self.run(params)

    def runLocal(self):
      #self.runLocalNetcodingCbr()
      self.runLocalCbr()
      #self.runLocalLinkProbe()

    def setSimParams(self, params):
      level = NONE
      params.handlerFieldLevel = level
      params.handlerRadioLevel = level
      params.handlerRadioDivLevel = level
      params.handlerMacLevel = level
      params.handlerRateLevel = level
      params.handlerNetLevel = ALL
      params.handlerRouteLevel= level
      params.handlerTransLevel = ALL
      params.handlerFlowLevel = ALL

      params.handlerRadioTimeBarLevel = level
      params.handlerMacTimeBarLevel = level
      params.handlerNetTimeBarLevel = level

      params.handlerForwardGraphLevel = BASIC
      params.handlerLinkTableLevel = level
      params.handlerLinkQuality = ALL
      return params



#---------------
# main
#---------------

if (1 < len(sys.argv)):
  driver = SimRemoteHiddenNode()
  driver.run(array(sys.argv[1:], String))
