From c1b57a315e9192c1cd8573bafe78c4c0702eb57f Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 20 Mar 2009 16:49:49 +0100 Subject: [PATCH 01/49] rework olsr --- bindings/python/ns3_module_node.py | 3 +- bindings/python/ns3_module_olsr.py | 479 ++++++++++++++---- src/helper/olsr-helper.cc | 10 +- src/routing/olsr/olsr-agent-impl.cc | 266 ++++++++-- src/routing/olsr/olsr-agent-impl.h | 67 ++- src/routing/olsr/olsr-agent.cc | 36 -- src/routing/olsr/olsr-agent.h | 83 --- .../{repositories.h => olsr-repositories.h} | 2 +- src/routing/olsr/olsr-state.h | 2 +- src/routing/olsr/wscript | 7 +- 10 files changed, 666 insertions(+), 289 deletions(-) delete mode 100644 src/routing/olsr/olsr-agent.cc delete mode 100644 src/routing/olsr/olsr-agent.h rename src/routing/olsr/{repositories.h => olsr-repositories.h} (99%) diff --git a/bindings/python/ns3_module_node.py b/bindings/python/ns3_module_node.py index b214fcda6..72cc4cd97 100644 --- a/bindings/python/ns3_module_node.py +++ b/bindings/python/ns3_module_node.py @@ -133,6 +133,8 @@ def register_types(module): module.add_class('SimpleChannel', parent=root_module['ns3::Channel']) ## simple-net-device.h: ns3::SimpleNetDevice [class] module.add_class('SimpleNetDevice', parent=root_module['ns3::NetDevice']) + module.add_container('ns3::olsr::MprSet', 'ns3::Ipv4Address', container_type='set') + module.add_container('std::vector< ns3::Ipv4Address >', 'ns3::Ipv4Address', container_type='vector') ## Register a nested module for the namespace Config @@ -173,7 +175,6 @@ def register_types_ns3_internal(module): def register_types_ns3_olsr(module): root_module = module.get_root() - module.add_container('std::vector< ns3::Ipv4Address >', 'ns3::Ipv4Address', container_type='vector') def register_methods(root_module): register_Ns3Address_methods(root_module, root_module['ns3::Address']) diff --git a/bindings/python/ns3_module_olsr.py b/bindings/python/ns3_module_olsr.py index 7f269fbef..5e5f2e2a3 100644 --- a/bindings/python/ns3_module_olsr.py +++ b/bindings/python/ns3_module_olsr.py @@ -3,6 +3,14 @@ from pybindgen import Module, FileCodeSink, param, retval, cppclass def register_types(module): root_module = module.get_root() + ## olsr-state.h: ns3::OlsrState [class] + module.add_class('OlsrState') + module.add_container('std::vector< ns3::olsr::MprSelectorTuple >', 'ns3::olsr::MprSelectorTuple', container_type='vector') + module.add_container('std::vector< ns3::olsr::NeighborTuple >', 'ns3::olsr::NeighborTuple', container_type='vector') + module.add_container('std::vector< ns3::olsr::TwoHopNeighborTuple >', 'ns3::olsr::TwoHopNeighborTuple', container_type='vector') + module.add_container('std::vector< ns3::olsr::LinkTuple >', 'ns3::olsr::LinkTuple', container_type='vector') + module.add_container('std::vector< ns3::olsr::TopologyTuple >', 'ns3::olsr::TopologyTuple', container_type='vector') + module.add_container('std::vector< ns3::olsr::IfaceAssocTuple >', 'ns3::olsr::IfaceAssocTuple', container_type='vector') ## Register a nested module for the namespace Config @@ -43,8 +51,14 @@ def register_types_ns3_internal(module): def register_types_ns3_olsr(module): root_module = module.get_root() - ## olsr-agent.h: ns3::olsr::Agent [class] - module.add_class('Agent', parent=root_module['ns3::Object']) + ## olsr-agent-impl.h: ns3::olsr::AgentImpl [class] + module.add_class('AgentImpl', parent=root_module['ns3::Ipv4RoutingProtocol']) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple [struct] + module.add_class('DuplicateTuple') + ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple [struct] + module.add_class('IfaceAssocTuple') + ## olsr-repositories.h: ns3::olsr::LinkTuple [struct] + module.add_class('LinkTuple') ## olsr-header.h: ns3::olsr::MessageHeader [class] module.add_class('MessageHeader', parent=root_module['ns3::Header']) ## olsr-header.h: ns3::olsr::MessageHeader::MessageType [enumeration] @@ -61,18 +75,29 @@ def register_types_ns3_olsr(module): module.add_class('Mid', outer_class=root_module['ns3::olsr::MessageHeader']) ## olsr-header.h: ns3::olsr::MessageHeader::Tc [struct] module.add_class('Tc', outer_class=root_module['ns3::olsr::MessageHeader']) + ## olsr-repositories.h: ns3::olsr::MprSelectorTuple [struct] + module.add_class('MprSelectorTuple') + ## olsr-repositories.h: ns3::olsr::NeighborTuple [struct] + module.add_class('NeighborTuple') + ## olsr-repositories.h: ns3::olsr::NeighborTuple::Status [enumeration] + module.add_enum('Status', ['STATUS_NOT_SYM', 'STATUS_SYM'], outer_class=root_module['ns3::olsr::NeighborTuple']) ## olsr-header.h: ns3::olsr::PacketHeader [class] module.add_class('PacketHeader', parent=root_module['ns3::Header']) - ## olsr-routing-table.h: ns3::olsr::RoutingTable [class] - module.add_class('RoutingTable', parent=root_module['ns3::Ipv4RoutingProtocol']) - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry [struct] + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry [struct] module.add_class('RoutingTableEntry') + ## olsr-repositories.h: ns3::olsr::TopologyTuple [struct] + module.add_class('TopologyTuple') + ## olsr-repositories.h: ns3::olsr::TwoHopNeighborTuple [struct] + module.add_class('TwoHopNeighborTuple') module.add_container('std::vector< ns3::olsr::MessageHeader::Hello::LinkMessage >', 'ns3::olsr::MessageHeader::Hello::LinkMessage', container_type='vector') module.add_container('std::vector< ns3::olsr::MessageHeader::Hna::Association >', 'ns3::olsr::MessageHeader::Hna::Association', container_type='vector') - module.add_container('std::vector< ns3::olsr::RoutingTableEntry >', 'ns3::olsr::RoutingTableEntry', container_type='vector') def register_methods(root_module): - register_Ns3OlsrAgent_methods(root_module, root_module['ns3::olsr::Agent']) + register_Ns3OlsrState_methods(root_module, root_module['ns3::OlsrState']) + register_Ns3OlsrAgentImpl_methods(root_module, root_module['ns3::olsr::AgentImpl']) + register_Ns3OlsrDuplicateTuple_methods(root_module, root_module['ns3::olsr::DuplicateTuple']) + register_Ns3OlsrIfaceAssocTuple_methods(root_module, root_module['ns3::olsr::IfaceAssocTuple']) + register_Ns3OlsrLinkTuple_methods(root_module, root_module['ns3::olsr::LinkTuple']) register_Ns3OlsrMessageHeader_methods(root_module, root_module['ns3::olsr::MessageHeader']) register_Ns3OlsrMessageHeaderHello_methods(root_module, root_module['ns3::olsr::MessageHeader::Hello']) register_Ns3OlsrMessageHeaderHelloLinkMessage_methods(root_module, root_module['ns3::olsr::MessageHeader::Hello::LinkMessage']) @@ -80,41 +105,296 @@ def register_methods(root_module): register_Ns3OlsrMessageHeaderHnaAssociation_methods(root_module, root_module['ns3::olsr::MessageHeader::Hna::Association']) register_Ns3OlsrMessageHeaderMid_methods(root_module, root_module['ns3::olsr::MessageHeader::Mid']) register_Ns3OlsrMessageHeaderTc_methods(root_module, root_module['ns3::olsr::MessageHeader::Tc']) + register_Ns3OlsrMprSelectorTuple_methods(root_module, root_module['ns3::olsr::MprSelectorTuple']) + register_Ns3OlsrNeighborTuple_methods(root_module, root_module['ns3::olsr::NeighborTuple']) register_Ns3OlsrPacketHeader_methods(root_module, root_module['ns3::olsr::PacketHeader']) - register_Ns3OlsrRoutingTable_methods(root_module, root_module['ns3::olsr::RoutingTable']) register_Ns3OlsrRoutingTableEntry_methods(root_module, root_module['ns3::olsr::RoutingTableEntry']) + register_Ns3OlsrTopologyTuple_methods(root_module, root_module['ns3::olsr::TopologyTuple']) + register_Ns3OlsrTwoHopNeighborTuple_methods(root_module, root_module['ns3::olsr::TwoHopNeighborTuple']) return -def register_Ns3OlsrAgent_methods(root_module, cls): - ## olsr-agent.h: ns3::olsr::Agent::Agent(ns3::olsr::Agent const & arg0) [copy constructor] - cls.add_constructor([param('ns3::olsr::Agent const &', 'arg0')]) - ## olsr-agent.h: ns3::olsr::Agent::Agent() [constructor] +def register_Ns3OlsrState_methods(root_module, cls): + ## olsr-state.h: ns3::OlsrState::OlsrState(ns3::OlsrState const & arg0) [copy constructor] + cls.add_constructor([param('ns3::OlsrState const &', 'arg0')]) + ## olsr-state.h: ns3::OlsrState::OlsrState() [constructor] cls.add_constructor([]) - ## olsr-agent.h: static ns3::TypeId ns3::olsr::Agent::GetTypeId() [member function] + ## olsr-state.h: ns3::olsr::MprSelectorSet const & ns3::OlsrState::GetMprSelectors() const [member function] + cls.add_method('GetMprSelectors', + 'ns3::olsr::MprSelectorSet const &', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::MprSelectorTuple * ns3::OlsrState::FindMprSelectorTuple(ns3::Ipv4Address const & mainAddr) [member function] + cls.add_method('FindMprSelectorTuple', + 'ns3::olsr::MprSelectorTuple *', + [param('ns3::Ipv4Address const &', 'mainAddr')]) + ## olsr-state.h: void ns3::OlsrState::EraseMprSelectorTuple(ns3::olsr::MprSelectorTuple const & tuple) [member function] + cls.add_method('EraseMprSelectorTuple', + 'void', + [param('ns3::olsr::MprSelectorTuple const &', 'tuple')]) + ## olsr-state.h: void ns3::OlsrState::EraseMprSelectorTuples(ns3::Ipv4Address const & mainAddr) [member function] + cls.add_method('EraseMprSelectorTuples', + 'void', + [param('ns3::Ipv4Address const &', 'mainAddr')]) + ## olsr-state.h: void ns3::OlsrState::InsertMprSelectorTuple(ns3::olsr::MprSelectorTuple const & tuple) [member function] + cls.add_method('InsertMprSelectorTuple', + 'void', + [param('ns3::olsr::MprSelectorTuple const &', 'tuple')]) + ## olsr-state.h: std::string ns3::OlsrState::PrintMprSelectorSet() const [member function] + cls.add_method('PrintMprSelectorSet', + 'std::string', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::NeighborSet const & ns3::OlsrState::GetNeighbors() const [member function] + cls.add_method('GetNeighbors', + 'ns3::olsr::NeighborSet const &', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::NeighborSet & ns3::OlsrState::GetNeighbors() [member function] + cls.add_method('GetNeighbors', + 'ns3::olsr::NeighborSet &', + []) + ## olsr-state.h: ns3::olsr::NeighborTuple * ns3::OlsrState::FindNeighborTuple(ns3::Ipv4Address const & mainAddr) [member function] + cls.add_method('FindNeighborTuple', + 'ns3::olsr::NeighborTuple *', + [param('ns3::Ipv4Address const &', 'mainAddr')]) + ## olsr-state.h: ns3::olsr::NeighborTuple const * ns3::OlsrState::FindSymNeighborTuple(ns3::Ipv4Address const & mainAddr) const [member function] + cls.add_method('FindSymNeighborTuple', + 'ns3::olsr::NeighborTuple const *', + [param('ns3::Ipv4Address const &', 'mainAddr')], + is_const=True) + ## olsr-state.h: ns3::olsr::NeighborTuple * ns3::OlsrState::FindNeighborTuple(ns3::Ipv4Address const & mainAddr, uint8_t willingness) [member function] + cls.add_method('FindNeighborTuple', + 'ns3::olsr::NeighborTuple *', + [param('ns3::Ipv4Address const &', 'mainAddr'), param('uint8_t', 'willingness')]) + ## olsr-state.h: void ns3::OlsrState::EraseNeighborTuple(ns3::olsr::NeighborTuple const & neighborTuple) [member function] + cls.add_method('EraseNeighborTuple', + 'void', + [param('ns3::olsr::NeighborTuple const &', 'neighborTuple')]) + ## olsr-state.h: void ns3::OlsrState::EraseNeighborTuple(ns3::Ipv4Address const & mainAddr) [member function] + cls.add_method('EraseNeighborTuple', + 'void', + [param('ns3::Ipv4Address const &', 'mainAddr')]) + ## olsr-state.h: void ns3::OlsrState::InsertNeighborTuple(ns3::olsr::NeighborTuple const & tuple) [member function] + cls.add_method('InsertNeighborTuple', + 'void', + [param('ns3::olsr::NeighborTuple const &', 'tuple')]) + ## olsr-state.h: ns3::olsr::TwoHopNeighborSet const & ns3::OlsrState::GetTwoHopNeighbors() const [member function] + cls.add_method('GetTwoHopNeighbors', + 'ns3::olsr::TwoHopNeighborSet const &', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::TwoHopNeighborSet & ns3::OlsrState::GetTwoHopNeighbors() [member function] + cls.add_method('GetTwoHopNeighbors', + 'ns3::olsr::TwoHopNeighborSet &', + []) + ## olsr-state.h: ns3::olsr::TwoHopNeighborTuple * ns3::OlsrState::FindTwoHopNeighborTuple(ns3::Ipv4Address const & neighbor, ns3::Ipv4Address const & twoHopNeighbor) [member function] + cls.add_method('FindTwoHopNeighborTuple', + 'ns3::olsr::TwoHopNeighborTuple *', + [param('ns3::Ipv4Address const &', 'neighbor'), param('ns3::Ipv4Address const &', 'twoHopNeighbor')]) + ## olsr-state.h: void ns3::OlsrState::EraseTwoHopNeighborTuple(ns3::olsr::TwoHopNeighborTuple const & tuple) [member function] + cls.add_method('EraseTwoHopNeighborTuple', + 'void', + [param('ns3::olsr::TwoHopNeighborTuple const &', 'tuple')]) + ## olsr-state.h: void ns3::OlsrState::EraseTwoHopNeighborTuples(ns3::Ipv4Address const & neighbor) [member function] + cls.add_method('EraseTwoHopNeighborTuples', + 'void', + [param('ns3::Ipv4Address const &', 'neighbor')]) + ## olsr-state.h: void ns3::OlsrState::EraseTwoHopNeighborTuples(ns3::Ipv4Address const & neighbor, ns3::Ipv4Address const & twoHopNeighbor) [member function] + cls.add_method('EraseTwoHopNeighborTuples', + 'void', + [param('ns3::Ipv4Address const &', 'neighbor'), param('ns3::Ipv4Address const &', 'twoHopNeighbor')]) + ## olsr-state.h: void ns3::OlsrState::InsertTwoHopNeighborTuple(ns3::olsr::TwoHopNeighborTuple const & tuple) [member function] + cls.add_method('InsertTwoHopNeighborTuple', + 'void', + [param('ns3::olsr::TwoHopNeighborTuple const &', 'tuple')]) + ## olsr-state.h: bool ns3::OlsrState::FindMprAddress(ns3::Ipv4Address const & address) [member function] + cls.add_method('FindMprAddress', + 'bool', + [param('ns3::Ipv4Address const &', 'address')]) + ## olsr-state.h: void ns3::OlsrState::SetMprSet(ns3::olsr::MprSet mprSet) [member function] + cls.add_method('SetMprSet', + 'void', + [param('ns3::olsr::MprSet', 'mprSet')]) + ## olsr-state.h: ns3::olsr::DuplicateTuple * ns3::OlsrState::FindDuplicateTuple(ns3::Ipv4Address const & address, uint16_t sequenceNumber) [member function] + cls.add_method('FindDuplicateTuple', + 'ns3::olsr::DuplicateTuple *', + [param('ns3::Ipv4Address const &', 'address'), param('uint16_t', 'sequenceNumber')]) + ## olsr-state.h: void ns3::OlsrState::EraseDuplicateTuple(ns3::olsr::DuplicateTuple const & tuple) [member function] + cls.add_method('EraseDuplicateTuple', + 'void', + [param('ns3::olsr::DuplicateTuple const &', 'tuple')]) + ## olsr-state.h: void ns3::OlsrState::InsertDuplicateTuple(ns3::olsr::DuplicateTuple const & tuple) [member function] + cls.add_method('InsertDuplicateTuple', + 'void', + [param('ns3::olsr::DuplicateTuple const &', 'tuple')]) + ## olsr-state.h: ns3::olsr::LinkSet const & ns3::OlsrState::GetLinks() const [member function] + cls.add_method('GetLinks', + 'ns3::olsr::LinkSet const &', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::LinkTuple * ns3::OlsrState::FindLinkTuple(ns3::Ipv4Address const & ifaceAddr) [member function] + cls.add_method('FindLinkTuple', + 'ns3::olsr::LinkTuple *', + [param('ns3::Ipv4Address const &', 'ifaceAddr')]) + ## olsr-state.h: ns3::olsr::LinkTuple * ns3::OlsrState::FindSymLinkTuple(ns3::Ipv4Address const & ifaceAddr, ns3::Time time) [member function] + cls.add_method('FindSymLinkTuple', + 'ns3::olsr::LinkTuple *', + [param('ns3::Ipv4Address const &', 'ifaceAddr'), param('ns3::Time', 'time')]) + ## olsr-state.h: void ns3::OlsrState::EraseLinkTuple(ns3::olsr::LinkTuple const & tuple) [member function] + cls.add_method('EraseLinkTuple', + 'void', + [param('ns3::olsr::LinkTuple const &', 'tuple')]) + ## olsr-state.h: ns3::olsr::LinkTuple & ns3::OlsrState::InsertLinkTuple(ns3::olsr::LinkTuple const & tuple) [member function] + cls.add_method('InsertLinkTuple', + 'ns3::olsr::LinkTuple &', + [param('ns3::olsr::LinkTuple const &', 'tuple')]) + ## olsr-state.h: ns3::olsr::TopologySet const & ns3::OlsrState::GetTopologySet() const [member function] + cls.add_method('GetTopologySet', + 'ns3::olsr::TopologySet const &', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::TopologyTuple * ns3::OlsrState::FindTopologyTuple(ns3::Ipv4Address const & destAddr, ns3::Ipv4Address const & lastAddr) [member function] + cls.add_method('FindTopologyTuple', + 'ns3::olsr::TopologyTuple *', + [param('ns3::Ipv4Address const &', 'destAddr'), param('ns3::Ipv4Address const &', 'lastAddr')]) + ## olsr-state.h: ns3::olsr::TopologyTuple * ns3::OlsrState::FindNewerTopologyTuple(ns3::Ipv4Address const & lastAddr, uint16_t ansn) [member function] + cls.add_method('FindNewerTopologyTuple', + 'ns3::olsr::TopologyTuple *', + [param('ns3::Ipv4Address const &', 'lastAddr'), param('uint16_t', 'ansn')]) + ## olsr-state.h: void ns3::OlsrState::EraseTopologyTuple(ns3::olsr::TopologyTuple const & tuple) [member function] + cls.add_method('EraseTopologyTuple', + 'void', + [param('ns3::olsr::TopologyTuple const &', 'tuple')]) + ## olsr-state.h: void ns3::OlsrState::EraseOlderTopologyTuples(ns3::Ipv4Address const & lastAddr, uint16_t ansn) [member function] + cls.add_method('EraseOlderTopologyTuples', + 'void', + [param('ns3::Ipv4Address const &', 'lastAddr'), param('uint16_t', 'ansn')]) + ## olsr-state.h: void ns3::OlsrState::InsertTopologyTuple(ns3::olsr::TopologyTuple const & tuple) [member function] + cls.add_method('InsertTopologyTuple', + 'void', + [param('ns3::olsr::TopologyTuple const &', 'tuple')]) + ## olsr-state.h: ns3::olsr::IfaceAssocSet const & ns3::OlsrState::GetIfaceAssocSet() const [member function] + cls.add_method('GetIfaceAssocSet', + 'ns3::olsr::IfaceAssocSet const &', + [], + is_const=True) + ## olsr-state.h: ns3::olsr::IfaceAssocSet & ns3::OlsrState::GetIfaceAssocSetMutable() [member function] + cls.add_method('GetIfaceAssocSetMutable', + 'ns3::olsr::IfaceAssocSet &', + []) + ## olsr-state.h: ns3::olsr::IfaceAssocTuple * ns3::OlsrState::FindIfaceAssocTuple(ns3::Ipv4Address const & ifaceAddr) [member function] + cls.add_method('FindIfaceAssocTuple', + 'ns3::olsr::IfaceAssocTuple *', + [param('ns3::Ipv4Address const &', 'ifaceAddr')]) + ## olsr-state.h: ns3::olsr::IfaceAssocTuple const * ns3::OlsrState::FindIfaceAssocTuple(ns3::Ipv4Address const & ifaceAddr) const [member function] + cls.add_method('FindIfaceAssocTuple', + 'ns3::olsr::IfaceAssocTuple const *', + [param('ns3::Ipv4Address const &', 'ifaceAddr')], + is_const=True) + ## olsr-state.h: void ns3::OlsrState::EraseIfaceAssocTuple(ns3::olsr::IfaceAssocTuple const & tuple) [member function] + cls.add_method('EraseIfaceAssocTuple', + 'void', + [param('ns3::olsr::IfaceAssocTuple const &', 'tuple')]) + ## olsr-state.h: void ns3::OlsrState::InsertIfaceAssocTuple(ns3::olsr::IfaceAssocTuple const & tuple) [member function] + cls.add_method('InsertIfaceAssocTuple', + 'void', + [param('ns3::olsr::IfaceAssocTuple const &', 'tuple')]) + ## olsr-state.h: std::vector > ns3::OlsrState::FindNeighborInterfaces(ns3::Ipv4Address const & neighborMainAddr) const [member function] + cls.add_method('FindNeighborInterfaces', + 'std::vector< ns3::Ipv4Address >', + [param('ns3::Ipv4Address const &', 'neighborMainAddr')], + is_const=True) + return + +def register_Ns3OlsrAgentImpl_methods(root_module, cls): + ## olsr-agent-impl.h: ns3::olsr::AgentImpl::AgentImpl(ns3::olsr::AgentImpl const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::AgentImpl const &', 'arg0')]) + ## olsr-agent-impl.h: static ns3::TypeId ns3::olsr::AgentImpl::GetTypeId() [member function] cls.add_method('GetTypeId', 'ns3::TypeId', [], is_static=True) - ## olsr-agent.h: void ns3::olsr::Agent::SetNode(ns3::Ptr node) [member function] + ## olsr-agent-impl.h: ns3::olsr::AgentImpl::AgentImpl() [constructor] + cls.add_constructor([]) + ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::SetNode(ns3::Ptr node) [member function] cls.add_method('SetNode', 'void', - [param('ns3::Ptr< ns3::Node >', 'node')], - is_pure_virtual=True, is_virtual=True) - ## olsr-agent.h: void ns3::olsr::Agent::SetMainInterface(uint32_t interface) [member function] - cls.add_method('SetMainInterface', - 'void', - [param('uint32_t', 'interface')], - is_pure_virtual=True, is_virtual=True) - ## olsr-agent.h: void ns3::olsr::Agent::Start() [member function] + [param('ns3::Ptr< ns3::Node >', 'node')]) + ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::Start() [member function] cls.add_method('Start', + 'void', + []) + ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::SetMainInterface(uint32_t interface) [member function] + cls.add_method('SetMainInterface', + 'void', + [param('uint32_t', 'interface')]) + ## olsr-agent-impl.h: bool ns3::olsr::AgentImpl::RequestRoute(uint32_t ifIndex, ns3::Ipv4Header const & ipHeader, ns3::Ptr packet, ns3::Callback,const ns3::Ipv4Header&,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> routeReply) [member function] + cls.add_method('RequestRoute', + 'bool', + [param('uint32_t', 'ifIndex'), param('ns3::Ipv4Header const &', 'ipHeader'), param('ns3::Ptr< ns3::Packet >', 'packet'), param('ns3::Callback< void, bool, ns3::Ipv4Route const &, ns3::Ptr< ns3::Packet >, ns3::Ipv4Header const &, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'routeReply')], + visibility='private', is_virtual=True) + ## olsr-agent-impl.h: bool ns3::olsr::AgentImpl::RequestIfIndex(ns3::Ipv4Address destination, uint32_t & ifIndex) [member function] + cls.add_method('RequestIfIndex', + 'bool', + [param('ns3::Ipv4Address', 'destination'), param('uint32_t &', 'ifIndex')], + visibility='private', is_virtual=True) + ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::DoDispose() [member function] + cls.add_method('DoDispose', 'void', [], - is_pure_virtual=True, is_virtual=True) - ## olsr-agent.h: ns3::Ptr ns3::olsr::Agent::GetRoutingTable() const [member function] - cls.add_method('GetRoutingTable', - 'ns3::Ptr< ns3::olsr::RoutingTable const >', - [], - is_pure_virtual=True, is_const=True, is_virtual=True) + visibility='private', is_virtual=True) + return + +def register_Ns3OlsrDuplicateTuple_methods(root_module, cls): + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::DuplicateTuple() [constructor] + cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::DuplicateTuple(ns3::olsr::DuplicateTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::DuplicateTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::address [variable] + cls.add_instance_attribute('address', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::expirationTime [variable] + cls.add_instance_attribute('expirationTime', 'ns3::Time', is_const=False) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::ifaceList [variable] + cls.add_instance_attribute('ifaceList', 'std::vector< ns3::Ipv4Address >', is_const=False) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::retransmitted [variable] + cls.add_instance_attribute('retransmitted', 'bool', is_const=False) + ## olsr-repositories.h: ns3::olsr::DuplicateTuple::sequenceNumber [variable] + cls.add_instance_attribute('sequenceNumber', 'uint16_t', is_const=False) + return + +def register_Ns3OlsrIfaceAssocTuple_methods(root_module, cls): + cls.add_output_stream_operator() + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple::IfaceAssocTuple() [constructor] + cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple::IfaceAssocTuple(ns3::olsr::IfaceAssocTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::IfaceAssocTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple::ifaceAddr [variable] + cls.add_instance_attribute('ifaceAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple::mainAddr [variable] + cls.add_instance_attribute('mainAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple::time [variable] + cls.add_instance_attribute('time', 'ns3::Time', is_const=False) + return + +def register_Ns3OlsrLinkTuple_methods(root_module, cls): + cls.add_output_stream_operator() + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::LinkTuple::LinkTuple() [constructor] + cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::LinkTuple::LinkTuple(ns3::olsr::LinkTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::LinkTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::LinkTuple::asymTime [variable] + cls.add_instance_attribute('asymTime', 'ns3::Time', is_const=False) + ## olsr-repositories.h: ns3::olsr::LinkTuple::localIfaceAddr [variable] + cls.add_instance_attribute('localIfaceAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::LinkTuple::neighborIfaceAddr [variable] + cls.add_instance_attribute('neighborIfaceAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::LinkTuple::symTime [variable] + cls.add_instance_attribute('symTime', 'ns3::Time', is_const=False) + ## olsr-repositories.h: ns3::olsr::LinkTuple::time [variable] + cls.add_instance_attribute('time', 'ns3::Time', is_const=False) return def register_Ns3OlsrMessageHeader_methods(root_module, cls): @@ -394,6 +674,33 @@ def register_Ns3OlsrMessageHeaderTc_methods(root_module, cls): cls.add_instance_attribute('neighborAddresses', 'std::vector< ns3::Ipv4Address >', is_const=False) return +def register_Ns3OlsrMprSelectorTuple_methods(root_module, cls): + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::MprSelectorTuple::MprSelectorTuple() [constructor] + cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::MprSelectorTuple::MprSelectorTuple(ns3::olsr::MprSelectorTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::MprSelectorTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::MprSelectorTuple::expirationTime [variable] + cls.add_instance_attribute('expirationTime', 'ns3::Time', is_const=False) + ## olsr-repositories.h: ns3::olsr::MprSelectorTuple::mainAddr [variable] + cls.add_instance_attribute('mainAddr', 'ns3::Ipv4Address', is_const=False) + return + +def register_Ns3OlsrNeighborTuple_methods(root_module, cls): + cls.add_output_stream_operator() + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::NeighborTuple::NeighborTuple() [constructor] + cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::NeighborTuple::NeighborTuple(ns3::olsr::NeighborTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::NeighborTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::NeighborTuple::neighborMainAddr [variable] + cls.add_instance_attribute('neighborMainAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::NeighborTuple::status [variable] + cls.add_instance_attribute('status', 'ns3::olsr::NeighborTuple::Status', is_const=False) + ## olsr-repositories.h: ns3::olsr::NeighborTuple::willingness [variable] + cls.add_instance_attribute('willingness', 'uint8_t', is_const=False) + return + def register_Ns3OlsrPacketHeader_methods(root_module, cls): cls.add_output_stream_operator() ## olsr-header.h: ns3::olsr::PacketHeader::PacketHeader(ns3::olsr::PacketHeader const & arg0) [copy constructor] @@ -450,85 +757,51 @@ def register_Ns3OlsrPacketHeader_methods(root_module, cls): [param('uint16_t', 'seqnum')]) return -def register_Ns3OlsrRoutingTable_methods(root_module, cls): - ## olsr-routing-table.h: ns3::olsr::RoutingTable::RoutingTable(ns3::olsr::RoutingTable const & arg0) [copy constructor] - cls.add_constructor([param('ns3::olsr::RoutingTable const &', 'arg0')]) - ## olsr-routing-table.h: ns3::olsr::RoutingTable::RoutingTable() [constructor] +def register_Ns3OlsrRoutingTableEntry_methods(root_module, cls): + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::destAddr [variable] + cls.add_instance_attribute('destAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::nextAddr [variable] + cls.add_instance_attribute('nextAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::interface [variable] + cls.add_instance_attribute('interface', 'uint32_t', is_const=False) + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::distance [variable] + cls.add_instance_attribute('distance', 'uint32_t', is_const=False) + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry(ns3::olsr::RoutingTableEntry const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::RoutingTableEntry const &', 'arg0')]) + ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry() [constructor] cls.add_constructor([]) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::SetIpv4(ns3::Ptr ipv4) [member function] - cls.add_method('SetIpv4', - 'void', - [param('ns3::Ptr< ns3::Ipv4 >', 'ipv4')]) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::SetMainAddress(ns3::Ipv4Address mainAddress) [member function] - cls.add_method('SetMainAddress', - 'void', - [param('ns3::Ipv4Address', 'mainAddress')]) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::Clear() [member function] - cls.add_method('Clear', - 'void', - []) - ## olsr-routing-table.h: uint32_t ns3::olsr::RoutingTable::GetSize() const [member function] - cls.add_method('GetSize', - 'uint32_t', - [], - is_const=True) - ## olsr-routing-table.h: std::vector > ns3::olsr::RoutingTable::GetEntries() const [member function] - cls.add_method('GetEntries', - 'std::vector< ns3::olsr::RoutingTableEntry >', - [], - is_const=True) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::RemoveEntry(ns3::Ipv4Address const & dest) [member function] - cls.add_method('RemoveEntry', - 'void', - [param('ns3::Ipv4Address const &', 'dest')]) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::AddEntry(ns3::Ipv4Address const & dest, ns3::Ipv4Address const & next, uint32_t interface, uint32_t distance) [member function] - cls.add_method('AddEntry', - 'void', - [param('ns3::Ipv4Address const &', 'dest'), param('ns3::Ipv4Address const &', 'next'), param('uint32_t', 'interface'), param('uint32_t', 'distance')]) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::AddEntry(ns3::Ipv4Address const & dest, ns3::Ipv4Address const & next, ns3::Ipv4Address const & interfaceAddress, uint32_t distance) [member function] - cls.add_method('AddEntry', - 'void', - [param('ns3::Ipv4Address const &', 'dest'), param('ns3::Ipv4Address const &', 'next'), param('ns3::Ipv4Address const &', 'interfaceAddress'), param('uint32_t', 'distance')]) - ## olsr-routing-table.h: bool ns3::olsr::RoutingTable::Lookup(ns3::Ipv4Address const & dest, ns3::olsr::RoutingTableEntry & outEntry) const [member function] - cls.add_method('Lookup', - 'bool', - [param('ns3::Ipv4Address const &', 'dest'), param('ns3::olsr::RoutingTableEntry &', 'outEntry')], - is_const=True) - ## olsr-routing-table.h: bool ns3::olsr::RoutingTable::FindSendEntry(ns3::olsr::RoutingTableEntry const & entry, ns3::olsr::RoutingTableEntry & outEntry) const [member function] - cls.add_method('FindSendEntry', - 'bool', - [param('ns3::olsr::RoutingTableEntry const &', 'entry'), param('ns3::olsr::RoutingTableEntry &', 'outEntry')], - is_const=True) - ## olsr-routing-table.h: bool ns3::olsr::RoutingTable::RequestRoute(uint32_t ifIndex, ns3::Ipv4Header const & ipHeader, ns3::Ptr packet, ns3::Callback,const ns3::Ipv4Header&,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> routeReply) [member function] - cls.add_method('RequestRoute', - 'bool', - [param('uint32_t', 'ifIndex'), param('ns3::Ipv4Header const &', 'ipHeader'), param('ns3::Ptr< ns3::Packet >', 'packet'), param('ns3::Callback< void, bool, ns3::Ipv4Route const &, ns3::Ptr< ns3::Packet >, ns3::Ipv4Header const &, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'routeReply')], - is_virtual=True) - ## olsr-routing-table.h: bool ns3::olsr::RoutingTable::RequestIfIndex(ns3::Ipv4Address destination, uint32_t & ifIndex) [member function] - cls.add_method('RequestIfIndex', - 'bool', - [param('ns3::Ipv4Address', 'destination'), param('uint32_t &', 'ifIndex')], - is_virtual=True) - ## olsr-routing-table.h: void ns3::olsr::RoutingTable::DoDispose() [member function] - cls.add_method('DoDispose', - 'void', - [], - visibility='private', is_virtual=True) return -def register_Ns3OlsrRoutingTableEntry_methods(root_module, cls): - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry::destAddr [variable] - cls.add_instance_attribute('destAddr', 'ns3::Ipv4Address', is_const=False) - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry::nextAddr [variable] - cls.add_instance_attribute('nextAddr', 'ns3::Ipv4Address', is_const=False) - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry::interface [variable] - cls.add_instance_attribute('interface', 'uint32_t', is_const=False) - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry::distance [variable] - cls.add_instance_attribute('distance', 'uint32_t', is_const=False) - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry(ns3::olsr::RoutingTableEntry const & arg0) [copy constructor] - cls.add_constructor([param('ns3::olsr::RoutingTableEntry const &', 'arg0')]) - ## olsr-routing-table.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry() [constructor] +def register_Ns3OlsrTopologyTuple_methods(root_module, cls): + cls.add_output_stream_operator() + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::TopologyTuple::TopologyTuple() [constructor] cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::TopologyTuple::TopologyTuple(ns3::olsr::TopologyTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::TopologyTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::TopologyTuple::destAddr [variable] + cls.add_instance_attribute('destAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::TopologyTuple::expirationTime [variable] + cls.add_instance_attribute('expirationTime', 'ns3::Time', is_const=False) + ## olsr-repositories.h: ns3::olsr::TopologyTuple::lastAddr [variable] + cls.add_instance_attribute('lastAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::TopologyTuple::sequenceNumber [variable] + cls.add_instance_attribute('sequenceNumber', 'uint16_t', is_const=False) + return + +def register_Ns3OlsrTwoHopNeighborTuple_methods(root_module, cls): + cls.add_output_stream_operator() + cls.add_binary_comparison_operator('==') + ## olsr-repositories.h: ns3::olsr::TwoHopNeighborTuple::TwoHopNeighborTuple() [constructor] + cls.add_constructor([]) + ## olsr-repositories.h: ns3::olsr::TwoHopNeighborTuple::TwoHopNeighborTuple(ns3::olsr::TwoHopNeighborTuple const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::TwoHopNeighborTuple const &', 'arg0')]) + ## olsr-repositories.h: ns3::olsr::TwoHopNeighborTuple::expirationTime [variable] + cls.add_instance_attribute('expirationTime', 'ns3::Time', is_const=False) + ## olsr-repositories.h: ns3::olsr::TwoHopNeighborTuple::neighborMainAddr [variable] + cls.add_instance_attribute('neighborMainAddr', 'ns3::Ipv4Address', is_const=False) + ## olsr-repositories.h: ns3::olsr::TwoHopNeighborTuple::twoHopNeighborAddr [variable] + cls.add_instance_attribute('twoHopNeighborAddr', 'ns3::Ipv4Address', is_const=False) return def register_functions(root_module): diff --git a/src/helper/olsr-helper.cc b/src/helper/olsr-helper.cc index fd033726d..98a735be5 100644 --- a/src/helper/olsr-helper.cc +++ b/src/helper/olsr-helper.cc @@ -18,7 +18,7 @@ * Author: Mathieu Lacage */ #include "olsr-helper.h" -#include "ns3/olsr-agent.h" +#include "ns3/olsr-agent-impl.h" #include "ns3/node-list.h" #include "ns3/names.h" @@ -63,15 +63,17 @@ OlsrHelper::Install (NodeContainer container) void OlsrHelper::Install (Ptr node) { - if (node->GetObject () != 0) + if (node->GetObject () != 0) { NS_FATAL_ERROR ("OlsrHelper::Install(): Aggregating " "an Olsr Agent to a node with an existing Olsr Agent"); return; } - Ptr agent = m_agentFactory.Create (); - agent->SetNode (node); + Ptr agent = m_agentFactory.Create (); node->AggregateObject (agent); + Ptr ipv4 = node->GetObject (); + ipv4->AddRoutingProtocol (agent, 10); + agent->SetNode (node); agent->Start (); } void diff --git a/src/routing/olsr/olsr-agent-impl.cc b/src/routing/olsr/olsr-agent-impl.cc index 6252f984d..8096ce543 100644 --- a/src/routing/olsr/olsr-agent-impl.cc +++ b/src/routing/olsr/olsr-agent-impl.cc @@ -44,6 +44,7 @@ #include "ns3/uinteger.h" #include "ns3/enum.h" #include "ns3/trace-source-accessor.h" +#include "ns3/ipv4-header.h" /********** Useful macros **********/ @@ -148,7 +149,7 @@ TypeId AgentImpl::GetTypeId (void) { static TypeId tid = TypeId ("ns3::olsr::AgentImpl") - .SetParent () + .SetParent () .AddConstructor () .AddAttribute ("HelloInterval", "HELLO messages emission interval.", TimeValue (Seconds (2)), @@ -221,13 +222,7 @@ void AgentImpl::DoDispose () } m_socketAddresses.clear (); - if (m_routingTable) - { - m_routingTable->Dispose (); - m_routingTable = 0; - } - - Object::DoDispose (); + Ipv4RoutingProtocol::DoDispose (); } void AgentImpl::Start () @@ -250,13 +245,6 @@ void AgentImpl::Start () NS_LOG_DEBUG ("Starting OLSR on node " << m_mainAddress); - m_routingTable = CreateObject (); - m_routingTable->SetIpv4 (m_ipv4); - m_routingTable->SetMainAddress (m_mainAddress); - // Add OLSR as routing protocol, with slightly higher priority than - // static routing. - m_ipv4->AddRoutingProtocol (m_routingTable, 10); - Ipv4Address loopback ("127.0.0.1"); for (uint32_t i = 0; i < m_ipv4->GetNInterfaces (); i++) { @@ -765,7 +753,7 @@ AgentImpl::RoutingTableComputation () << ": RoutingTableComputation begin..."); // 1. All the entries from the routing table are removed. - m_routingTable->Clear (); + Clear (); // 2. The new routing entries are added starting with the // symmetric neighbors (h=1) as the destination nodes. @@ -792,10 +780,10 @@ AgentImpl::RoutingTableComputation () NS_LOG_LOGIC ("Link tuple matches neighbor " << nb_tuple.neighborMainAddr << " => adding routing table entry to neighbor"); lt = &link_tuple; - m_routingTable->AddEntry (link_tuple.neighborIfaceAddr, - link_tuple.neighborIfaceAddr, - link_tuple.localIfaceAddr, - 1); + AddEntry (link_tuple.neighborIfaceAddr, + link_tuple.neighborIfaceAddr, + link_tuple.localIfaceAddr, + 1); if (link_tuple.neighborIfaceAddr == nb_tuple.neighborMainAddr) { nb_main_addr = true; @@ -823,10 +811,10 @@ AgentImpl::RoutingTableComputation () { NS_LOG_LOGIC ("no R_dest_addr is equal to the main address of the neighbor " "=> adding additional routing entry"); - m_routingTable->AddEntry(nb_tuple.neighborMainAddr, - lt->neighborIfaceAddr, - lt->localIfaceAddr, - 1); + AddEntry(nb_tuple.neighborMainAddr, + lt->neighborIfaceAddr, + lt->localIfaceAddr, + 1); } } } @@ -892,11 +880,11 @@ AgentImpl::RoutingTableComputation () // R_dest_addr == N_neighbor_main_addr // of the 2-hop tuple; RoutingTableEntry entry; - bool foundEntry = m_routingTable->Lookup (nb2hop_tuple.neighborMainAddr, entry); + bool foundEntry = Lookup (nb2hop_tuple.neighborMainAddr, entry); if (foundEntry) { NS_LOG_LOGIC ("Adding routing entry for two-hop neighbor."); - m_routingTable->AddEntry (nb2hop_tuple.twoHopNeighborAddr, + AddEntry (nb2hop_tuple.twoHopNeighborAddr, entry.nextAddr, entry.interface, 2); @@ -927,8 +915,8 @@ AgentImpl::RoutingTableComputation () NS_LOG_LOGIC ("Looking at topology tuple: " << topology_tuple); RoutingTableEntry destAddrEntry, lastAddrEntry; - bool have_destAddrEntry = m_routingTable->Lookup (topology_tuple.destAddr, destAddrEntry); - bool have_lastAddrEntry = m_routingTable->Lookup (topology_tuple.lastAddr, lastAddrEntry); + bool have_destAddrEntry = Lookup (topology_tuple.destAddr, destAddrEntry); + bool have_lastAddrEntry = Lookup (topology_tuple.lastAddr, lastAddrEntry); if (!have_destAddrEntry && have_lastAddrEntry && lastAddrEntry.distance == h) { NS_LOG_LOGIC ("Adding routing table entry based on the topology tuple."); @@ -942,10 +930,10 @@ AgentImpl::RoutingTableComputation () // R_iface_addr = R_iface_addr of the recorded // route entry where: // R_dest_addr == T_last_addr. - m_routingTable->AddEntry (topology_tuple.destAddr, - lastAddrEntry.nextAddr, - lastAddrEntry.interface, - h + 1); + AddEntry (topology_tuple.destAddr, + lastAddrEntry.nextAddr, + lastAddrEntry.interface, + h + 1); added = true; } else @@ -973,8 +961,8 @@ AgentImpl::RoutingTableComputation () { IfaceAssocTuple const &tuple = *it; RoutingTableEntry entry1, entry2; - bool have_entry1 = m_routingTable->Lookup (tuple.mainAddr, entry1); - bool have_entry2 = m_routingTable->Lookup (tuple.ifaceAddr, entry2); + bool have_entry1 = Lookup (tuple.mainAddr, entry1); + bool have_entry2 = Lookup (tuple.ifaceAddr, entry2); if (have_entry1 && !have_entry2) { // then a route entry is created in the routing table with: @@ -983,15 +971,15 @@ AgentImpl::RoutingTableComputation () // R_next_addr = R_next_addr (of the recorded route entry) // R_dist = R_dist (of the recorded route entry) // R_iface_addr = R_iface_addr (of the recorded route entry). - m_routingTable->AddEntry (tuple.ifaceAddr, - entry1.nextAddr, - entry1.interface, - entry1.distance); + AddEntry (tuple.ifaceAddr, + entry1.nextAddr, + entry1.interface, + entry1.distance); } } NS_LOG_DEBUG ("Node " << m_mainAddress << ": RoutingTableComputation end."); - m_routingTableChanged (m_routingTable->GetSize ()); + m_routingTableChanged (GetSize ()); } @@ -2488,12 +2476,206 @@ AgentImpl::IfaceAssocTupleTimerExpire (Ipv4Address ifaceAddr) } } -Ptr -AgentImpl::GetRoutingTable () const +/// +/// \brief Clears the routing table and frees the memory assigned to each one of its entries. +/// +void +AgentImpl::Clear () { - return m_routingTable; + NS_LOG_FUNCTION_NOARGS (); + m_table.clear (); } +/// +/// \brief Deletes the entry whose destination address is given. +/// \param dest address of the destination node. +/// +void +AgentImpl::RemoveEntry (Ipv4Address const &dest) +{ + m_table.erase (dest); +} + +/// +/// \brief Looks up an entry for the specified destination address. +/// \param dest destination address. +/// \param outEntry output parameter to hold the routing entry result, if fuond +/// \return true if found, false if not found +/// +bool +AgentImpl::Lookup (Ipv4Address const &dest, + RoutingTableEntry &outEntry) const +{ + // Get the iterator at "dest" position + std::map::const_iterator it = + m_table.find (dest); + // If there is no route to "dest", return NULL + if (it == m_table.end ()) + return false; + outEntry = it->second; + return true; +} + +/// +/// \brief Finds the appropiate entry which must be used in order to forward +/// a data packet to a next hop (given a destination). +/// +/// Imagine a routing table like this: [A,B] [B,C] [C,C]; being each pair of the +/// form [dest addr,next-hop addr]. In this case, if this function is invoked with +/// [A,B] then pair [C,C] is returned because C is the next hop that must be used +/// to forward a data packet destined to A. That is, C is a neighbor of this node, +/// but B isn't. This function finds the appropiate neighbor for forwarding a packet. +/// +/// \param entry the routing table entry which indicates the destination node +/// we are interested in. +/// \return the appropiate routing table entry which indicates the next +/// hop which must be used for forwarding a data packet, or NULL +/// if there is no such entry. +/// +bool +AgentImpl::FindSendEntry (RoutingTableEntry const &entry, + RoutingTableEntry &outEntry) const +{ + outEntry = entry; + while (outEntry.destAddr != outEntry.nextAddr) + { + if (not Lookup(outEntry.nextAddr, outEntry)) + return false; + } + return true; +} + + +bool +AgentImpl::RequestRoute (uint32_t ifIndex, + const Ipv4Header &ipHeader, + Ptr packet, + RouteReplyCallback routeReply) +{ + RoutingTableEntry entry1, entry2; + if (Lookup (ipHeader.GetDestination (), entry1)) + { + bool foundSendEntry = FindSendEntry (entry1, entry2); + if (!foundSendEntry) + NS_FATAL_ERROR ("FindSendEntry failure"); + + Ipv4Route route = Ipv4Route::CreateHostRouteTo + (ipHeader.GetDestination (), entry2.nextAddr, entry2.interface); + + NS_LOG_DEBUG ("Olsr node " << m_mainAddress + << ": RouteRequest for dest=" << ipHeader.GetDestination () + << " --> nestHop=" << entry2.nextAddr + << " interface=" << entry2.interface); + + routeReply (true, route, packet, ipHeader); + return true; + } + else + { +#ifdef NS3_LOG_ENABLE + NS_LOG_DEBUG ("Olsr node " << m_mainAddress + << ": RouteRequest for dest=" << ipHeader.GetDestination () + << " --> NOT FOUND; ** Dumping routing table..."); + for (std::map::const_iterator iter = m_table.begin (); + iter != m_table.end (); iter++) + { + NS_LOG_DEBUG ("dest=" << iter->first << " --> next=" << iter->second.nextAddr + << " via interface " << iter->second.interface); + } + + NS_LOG_DEBUG ("** Routing table dump end."); +#endif + return false; + } +} + +bool +AgentImpl::RequestIfIndex (Ipv4Address destination, + uint32_t& ifIndex) +{ + RoutingTableEntry entry1, entry2; + if (Lookup (destination, entry1)) + { + bool foundSendEntry = FindSendEntry (entry1, entry2); + if (!foundSendEntry) + NS_FATAL_ERROR ("FindSendEntry failure"); + ifIndex = entry2.interface; + return true; + } + else + { + return false; + } +} + + +/// +/// \brief Adds a new entry into the routing table. +/// +/// If an entry for the given destination existed, it is deleted and freed. +/// +/// \param dest address of the destination node. +/// \param next address of the next hop node. +/// \param iface address of the local interface. +/// \param dist distance to the destination node. +/// +void +AgentImpl::AddEntry (Ipv4Address const &dest, + Ipv4Address const &next, + uint32_t interface, + uint32_t distance) +{ + NS_LOG_FUNCTION (this << dest << next << interface << distance << m_mainAddress); + + NS_ASSERT (distance > 0); + + // Creates a new rt entry with specified values + RoutingTableEntry &entry = m_table[dest]; + + entry.destAddr = dest; + entry.nextAddr = next; + entry.interface = interface; + entry.distance = distance; +} + +void +AgentImpl::AddEntry (Ipv4Address const &dest, + Ipv4Address const &next, + Ipv4Address const &interfaceAddress, + uint32_t distance) +{ + NS_LOG_FUNCTION (this << dest << next << interfaceAddress << distance << m_mainAddress); + + NS_ASSERT (distance > 0); + NS_ASSERT (m_ipv4); + + RoutingTableEntry entry; + for (uint32_t i = 0; i < m_ipv4->GetNInterfaces (); i++) + { + if (m_ipv4->GetAddress (i) == interfaceAddress) + { + AddEntry (dest, next, i, distance); + return; + } + } + NS_ASSERT (false); // should not be reached + AddEntry (dest, next, 0, distance); +} + + +std::vector +AgentImpl::GetEntries () const +{ + std::vector retval; + for (std::map::const_iterator iter = m_table.begin (); + iter != m_table.end (); iter++) + { + retval.push_back (iter->second); + } + return retval; +} + + }} // namespace olsr, ns3 diff --git a/src/routing/olsr/olsr-agent-impl.h b/src/routing/olsr/olsr-agent-impl.h index 94effeba1..e4433625c 100644 --- a/src/routing/olsr/olsr-agent-impl.h +++ b/src/routing/olsr/olsr-agent-impl.h @@ -24,14 +24,9 @@ #ifndef __OLSR_AGENT_IMPL_H__ #define __OLSR_AGENT_IMPL_H__ -#include - -#include "olsr-agent.h" #include "olsr-header.h" #include "olsr-state.h" - -#include "olsr-routing-table.h" -#include "repositories.h" +#include "olsr-repositories.h" #include "ns3/object.h" #include "ns3/packet.h" @@ -40,13 +35,31 @@ #include "ns3/event-garbage-collector.h" #include "ns3/timer.h" #include "ns3/traced-callback.h" +#include "ns3/ipv4.h" + +#include +#include namespace ns3 { namespace olsr { -class AgentImpl : public Agent +/// An %OLSR's routing table entry. +struct RoutingTableEntry +{ + Ipv4Address destAddr; ///< Address of the destination node. + Ipv4Address nextAddr; ///< Address of the next hop. + uint32_t interface; ///< Interface index + uint32_t distance; ///< Distance in hops to the destination. + + RoutingTableEntry () : // default values + destAddr (), nextAddr (), + interface (0), distance (0) {}; +}; + + +class AgentImpl : public Ipv4RoutingProtocol { public: static TypeId GetTypeId (void); @@ -54,13 +67,14 @@ public: AgentImpl (); virtual ~AgentImpl (); - virtual void SetNode (Ptr node); + void SetNode (Ptr node); - virtual void Start (); - virtual void SetMainInterface (uint32_t interface); - virtual Ptr GetRoutingTable () const; + void Start (); + void SetMainInterface (uint32_t interface); private: + std::map m_table; ///< Data structure for the routing table. + EventGarbageCollector m_events; /// Address of the routing agent. @@ -82,14 +96,39 @@ private: /// Willingness for forwarding packets on behalf of other nodes. uint8_t m_willingness; - /// Routing table. - Ptr m_routingTable; /// Internal state with all needed data structs. OlsrState m_state; Ptr m_ipv4; -protected: +private: + + void Clear (); + uint32_t GetSize () const { return m_table.size (); } + std::vector GetEntries () const; + void RemoveEntry (const Ipv4Address &dest); + void AddEntry (const Ipv4Address &dest, + const Ipv4Address &next, + uint32_t interface, + uint32_t distance); + void AddEntry (const Ipv4Address &dest, + const Ipv4Address &next, + const Ipv4Address &interfaceAddress, + uint32_t distance); + bool Lookup (const Ipv4Address &dest, + RoutingTableEntry &outEntry) const; + bool FindSendEntry (const RoutingTableEntry &entry, + RoutingTableEntry &outEntry) const; + + // From Ipv4RoutingProtocol + virtual bool RequestRoute (uint32_t ifIndex, + const Ipv4Header &ipHeader, + Ptr packet, + RouteReplyCallback routeReply); + virtual bool RequestIfIndex (Ipv4Address destination, + uint32_t& ifIndex); + + void DoDispose (); void SendPacket (Ptr packet, const MessageList &containedMessages); diff --git a/src/routing/olsr/olsr-agent.cc b/src/routing/olsr/olsr-agent.cc deleted file mode 100644 index 7b3c200a3..000000000 --- a/src/routing/olsr/olsr-agent.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2007 INESC Porto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Gustavo J. A. M. Carneiro - */ - -#include "olsr-agent.h" - -namespace ns3 { -namespace olsr { - -NS_OBJECT_ENSURE_REGISTERED (Agent); - -TypeId -Agent::GetTypeId (void) -{ - static TypeId tid = TypeId ("ns3::olsr::Agent") - .SetParent (); - return tid; -} - -}} diff --git a/src/routing/olsr/olsr-agent.h b/src/routing/olsr/olsr-agent.h deleted file mode 100644 index ab1bec264..000000000 --- a/src/routing/olsr/olsr-agent.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2007 INESC Porto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Gustavo J. A. M. Carneiro - */ - -#ifndef OLSR_AGENT_H -#define OLSR_AGENT_H - -#include "ns3/node.h" -#include "ns3/olsr-routing-table.h" - -namespace ns3 { -namespace olsr { - -/** - * \brief Class implementing the OLSR state machine - * - * This class represents an instance of the OLSR protocol. It - * attaches itself to a Node, and its lifecycle is bound to that node. - * Normally the functions in the ns3::olsr namespace are more simple - * to use to start OLSR on nodes, but access to the underlying OLSR - * agent can be useful in order to customize the OLSR parameters. - * Example: - * - * \code - * Ptr olsr = Agend::CreateDefault (); - * agent->SetMainInterface (2); - * agent->Start (); - * \endcode - */ -class Agent : public Object -{ -public: - static TypeId GetTypeId (void); - - virtual void SetNode (Ptr node) = 0; - - /** - * \brief Sets the main interface to be used by OLSR - * - * Normally OLSR supports multiple interfaces, but the protocol - * requires the definition of a "main interface". This interface's - * IPv4 address provides the identity of the node, and all outgoing - * OLSR routing messages must have the main interface address, - * regardless of the actual interface used to transmit the packet. - * This method allows one to explicitly select an interface as the - * main interface. It must be called before the agent starts, but - * calling it is optional; if not called, the agent tries to guess - * and uses a suitable interface. - */ - virtual void SetMainInterface (uint32_t interface) = 0; - - /** - * \brief Starts the OLSR protocol operation - * - * Calling this method essentially bootstraps the OLSR protocol, and - * causes the agent to start broadcasting OLSR messages to - * neighbors, as well start listening to messages from neighbors. - */ - virtual void Start () = 0; - - virtual Ptr GetRoutingTable () const = 0; -}; - -}} // namespace olsr, ns3 - -#endif /* OLSR_AGENT_H */ - diff --git a/src/routing/olsr/repositories.h b/src/routing/olsr/olsr-repositories.h similarity index 99% rename from src/routing/olsr/repositories.h rename to src/routing/olsr/olsr-repositories.h index 386c7f221..4990a4094 100644 --- a/src/routing/olsr/repositories.h +++ b/src/routing/olsr/olsr-repositories.h @@ -21,7 +21,7 @@ */ /// -/// \file OLSR_repositories.h +/// \file OLSR_olsr-repositories.h /// \brief Here are defined all data structures needed by an OLSR node. /// diff --git a/src/routing/olsr/olsr-state.h b/src/routing/olsr/olsr-state.h index d647445fe..b2ea6749b 100644 --- a/src/routing/olsr/olsr-state.h +++ b/src/routing/olsr/olsr-state.h @@ -25,7 +25,7 @@ #ifndef __OLSR_STATE_H__ #define __OLSR_STATE_H__ -#include "repositories.h" +#include "olsr-repositories.h" namespace ns3 { diff --git a/src/routing/olsr/wscript b/src/routing/olsr/wscript index 52e835d5b..b46ffbb9e 100644 --- a/src/routing/olsr/wscript +++ b/src/routing/olsr/wscript @@ -6,16 +6,15 @@ def build(bld): module.source = [ 'olsr-header.cc', 'olsr-state.cc', - 'olsr-routing-table.cc', - 'olsr-agent.cc', 'olsr-agent-impl.cc', ] headers = bld.new_task_gen('ns3header') headers.module = 'olsr' headers.source = [ - 'olsr-agent.h', + 'olsr-agent-impl.h', 'olsr-header.h', - 'olsr-routing-table.h' + 'olsr-state.h', + 'olsr-repositories.h', ] From 75e4cf62a166778f86b47da492457c2f16e68f2d Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 23 Mar 2009 09:56:27 +0100 Subject: [PATCH 02/49] remove dead files --- src/routing/olsr/olsr-routing-table.cc | 332 ------------------------- src/routing/olsr/olsr-routing-table.h | 112 --------- 2 files changed, 444 deletions(-) delete mode 100644 src/routing/olsr/olsr-routing-table.cc delete mode 100644 src/routing/olsr/olsr-routing-table.h diff --git a/src/routing/olsr/olsr-routing-table.cc b/src/routing/olsr/olsr-routing-table.cc deleted file mode 100644 index 296a69745..000000000 --- a/src/routing/olsr/olsr-routing-table.cc +++ /dev/null @@ -1,332 +0,0 @@ -/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2004 Francisco J. Ros - * Copyright (c) 2007 INESC Porto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Authors: Francisco J. Ros - * Gustavo J. A. M. Carneiro - */ - -/// -/// \file OLSR_rtable.cc -/// \brief Implementation of our routing table. -/// - -#include "olsr-routing-table.h" -#include "ns3/packet.h" -#include "ns3/ipv4-header.h" -#include "ns3/log.h" - -namespace ns3 { namespace olsr { - -NS_LOG_COMPONENT_DEFINE ("OlsrRoutingTable"); - -/// -/// \brief Clears the routing table and frees the memory assigned to each one of its entries. -/// -void -RoutingTable::Clear () -{ - NS_LOG_FUNCTION_NOARGS (); - m_table.clear (); -} - -/// -/// \brief Deletes the entry whose destination address is given. -/// \param dest address of the destination node. -/// -void -RoutingTable::RemoveEntry (Ipv4Address const &dest) -{ - m_table.erase (dest); -} - -/// -/// \brief Looks up an entry for the specified destination address. -/// \param dest destination address. -/// \param outEntry output parameter to hold the routing entry result, if fuond -/// \return true if found, false if not found -/// -bool -RoutingTable::Lookup (Ipv4Address const &dest, - RoutingTableEntry &outEntry) const -{ - // Get the iterator at "dest" position - std::map::const_iterator it = - m_table.find (dest); - // If there is no route to "dest", return NULL - if (it == m_table.end ()) - return false; - outEntry = it->second; - return true; -} - -/// -/// \brief Finds the appropiate entry which must be used in order to forward -/// a data packet to a next hop (given a destination). -/// -/// Imagine a routing table like this: [A,B] [B,C] [C,C]; being each pair of the -/// form [dest addr,next-hop addr]. In this case, if this function is invoked with -/// [A,B] then pair [C,C] is returned because C is the next hop that must be used -/// to forward a data packet destined to A. That is, C is a neighbor of this node, -/// but B isn't. This function finds the appropiate neighbor for forwarding a packet. -/// -/// \param entry the routing table entry which indicates the destination node -/// we are interested in. -/// \return the appropiate routing table entry which indicates the next -/// hop which must be used for forwarding a data packet, or NULL -/// if there is no such entry. -/// -bool -RoutingTable::FindSendEntry (RoutingTableEntry const &entry, - RoutingTableEntry &outEntry) const -{ - outEntry = entry; - while (outEntry.destAddr != outEntry.nextAddr) - { - if (not Lookup(outEntry.nextAddr, outEntry)) - return false; - } - return true; -} - - -bool -RoutingTable::RequestRoute (uint32_t ifIndex, - const Ipv4Header &ipHeader, - Ptr packet, - RouteReplyCallback routeReply) -{ - RoutingTableEntry entry1, entry2; - if (Lookup (ipHeader.GetDestination (), entry1)) - { - bool foundSendEntry = FindSendEntry (entry1, entry2); - if (!foundSendEntry) - NS_FATAL_ERROR ("FindSendEntry failure"); - - Ipv4Route route = Ipv4Route::CreateHostRouteTo - (ipHeader.GetDestination (), entry2.nextAddr, entry2.interface); - - NS_LOG_DEBUG ("Olsr node " << m_mainAddress - << ": RouteRequest for dest=" << ipHeader.GetDestination () - << " --> nestHop=" << entry2.nextAddr - << " interface=" << entry2.interface); - - routeReply (true, route, packet, ipHeader); - return true; - } - else - { -#ifdef NS3_LOG_ENABLE - NS_LOG_DEBUG ("Olsr node " << m_mainAddress - << ": RouteRequest for dest=" << ipHeader.GetDestination () - << " --> NOT FOUND; ** Dumping routing table..."); - for (std::map::const_iterator iter = m_table.begin (); - iter != m_table.end (); iter++) - { - NS_LOG_DEBUG ("dest=" << iter->first << " --> next=" << iter->second.nextAddr - << " via interface " << iter->second.interface); - } - - NS_LOG_DEBUG ("** Routing table dump end."); -#endif - return false; - } -} - -bool -RoutingTable::RequestIfIndex (Ipv4Address destination, - uint32_t& ifIndex) -{ - RoutingTableEntry entry1, entry2; - if (Lookup (destination, entry1)) - { - bool foundSendEntry = FindSendEntry (entry1, entry2); - if (!foundSendEntry) - NS_FATAL_ERROR ("FindSendEntry failure"); - ifIndex = entry2.interface; - return true; - } - else - { - return false; - } -} - - -/// -/// \brief Adds a new entry into the routing table. -/// -/// If an entry for the given destination existed, it is deleted and freed. -/// -/// \param dest address of the destination node. -/// \param next address of the next hop node. -/// \param iface address of the local interface. -/// \param dist distance to the destination node. -/// -void -RoutingTable::AddEntry (Ipv4Address const &dest, - Ipv4Address const &next, - uint32_t interface, - uint32_t distance) -{ - NS_LOG_FUNCTION (this << dest << next << interface << distance << m_mainAddress); - - NS_ASSERT (distance > 0); - - // Creates a new rt entry with specified values - RoutingTableEntry &entry = m_table[dest]; - - entry.destAddr = dest; - entry.nextAddr = next; - entry.interface = interface; - entry.distance = distance; -} - -void -RoutingTable::AddEntry (Ipv4Address const &dest, - Ipv4Address const &next, - Ipv4Address const &interfaceAddress, - uint32_t distance) -{ - NS_LOG_FUNCTION (this << dest << next << interfaceAddress << distance << m_mainAddress); - - NS_ASSERT (distance > 0); - NS_ASSERT (m_ipv4); - - RoutingTableEntry entry; - for (uint32_t i = 0; i < m_ipv4->GetNInterfaces (); i++) - { - if (m_ipv4->GetAddress (i) == interfaceAddress) - { - AddEntry (dest, next, i, distance); - return; - } - } - NS_ASSERT (false); // should not be reached - AddEntry (dest, next, 0, distance); -} - -void -RoutingTable::SetMainAddress (Ipv4Address mainAddress) -{ - m_mainAddress = mainAddress; -} - -void -RoutingTable::SetIpv4 (Ptr ipv4) -{ - m_ipv4 = ipv4; -} - -std::vector -RoutingTable::GetEntries () const -{ - std::vector retval; - for (std::map::const_iterator iter = m_table.begin (); - iter != m_table.end (); iter++) - { - retval.push_back (iter->second); - } - return retval; -} - -/// -/// \brief Returns the number of entries in the routing table. -/// \return the number of entries in the routing table. -/// -// u_int32_t -// RoutingTable::size() { -// return rt_.size(); -// } - -}}; // namespace ns3, olsr - - - -#ifdef RUN_SELF_TESTS - - -#include "ns3/test.h" - - -namespace ns3 { namespace olsr { - -class OlsrRoutingTableTest : public ns3::Test { -private: -public: - OlsrRoutingTableTest (); - virtual bool RunTests (void); - - -}; - -OlsrRoutingTableTest::OlsrRoutingTableTest () - : ns3::Test ("OlsrRoutingTable") -{} - - -bool -OlsrRoutingTableTest::RunTests (void) -{ - bool result = true; - - RoutingTable table; - - table.AddEntry (Ipv4Address ("1.2.3.5"), - Ipv4Address ("1.2.3.4"), - 0, - 1); - - table.AddEntry (Ipv4Address ("1.2.3.4"), - Ipv4Address ("1.2.3.4"), - 0, - 1); - - RoutingTableEntry entry1; - NS_TEST_ASSERT (table.Lookup (Ipv4Address ("1.2.3.5"), entry1)); - NS_TEST_ASSERT_EQUAL (entry1.destAddr, Ipv4Address ("1.2.3.5")); - NS_TEST_ASSERT_EQUAL (entry1.nextAddr, Ipv4Address ("1.2.3.4")); - NS_TEST_ASSERT_EQUAL (entry1.interface, 0); - NS_TEST_ASSERT_EQUAL (entry1.distance, 1); - - RoutingTableEntry entry2; - NS_TEST_ASSERT (table.Lookup (Ipv4Address ("1.2.3.4"), entry2)); - NS_TEST_ASSERT_EQUAL (entry2.destAddr, Ipv4Address ("1.2.3.4")); - NS_TEST_ASSERT_EQUAL (entry2.nextAddr, Ipv4Address ("1.2.3.4")); - NS_TEST_ASSERT_EQUAL (entry2.interface, 0); - NS_TEST_ASSERT_EQUAL (entry2.distance, 1); - - RoutingTableEntry sendEntry; - NS_TEST_ASSERT (table.FindSendEntry (entry1, sendEntry)); - NS_TEST_ASSERT_EQUAL (sendEntry.destAddr, Ipv4Address ("1.2.3.4")); - NS_TEST_ASSERT_EQUAL (sendEntry.nextAddr, Ipv4Address ("1.2.3.4")); - NS_TEST_ASSERT_EQUAL (sendEntry.interface, 0); - NS_TEST_ASSERT_EQUAL (sendEntry.distance, 1); - - table.RemoveEntry (Ipv4Address ("1.2.3.5")); - RoutingTableEntry removedEntry; - NS_TEST_ASSERT (not table.Lookup (Ipv4Address ("1.2.3.5"), removedEntry)); - - return result; -} - -static OlsrRoutingTableTest gOlsrRoutingTableTest; - -}}; // namespace - - -#endif /* RUN_SELF_TESTS */ diff --git a/src/routing/olsr/olsr-routing-table.h b/src/routing/olsr/olsr-routing-table.h deleted file mode 100644 index bbd20a2bc..000000000 --- a/src/routing/olsr/olsr-routing-table.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2004 Francisco J. Ros - * Copyright (c) 2007 INESC Porto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Authors: Francisco J. Ros - * Gustavo J. A. M. Carneiro - */ - -/// -/// \file olsr-routing-table.h -/// \brief Header file for routing table's related stuff. -/// - -#ifndef __OLSR_RTABLE_H__ -#define __OLSR_RTABLE_H__ - -#include "ns3/ipv4.h" -#include - - -namespace ns3 { namespace olsr { - -/// An %OLSR's routing table entry. -struct RoutingTableEntry -{ - Ipv4Address destAddr; ///< Address of the destination node. - Ipv4Address nextAddr; ///< Address of the next hop. - uint32_t interface; ///< Interface index - uint32_t distance; ///< Distance in hops to the destination. - - RoutingTableEntry () : // default values - destAddr (), nextAddr (), - interface (0), distance (0) {}; -}; - -/// -/// \brief Defines rtable_t as a map of OLSR_rt_entry, whose key is the destination address. -/// -/// The routing table is thus defined as pairs: [dest address, entry]. Each element -/// of the pair can be accesed via "first" and "second" members. -/// -//typedef std::map RoutingTable; - -/// -/// \brief This class is a representation of the OLSR's Routing Table. -/// -class RoutingTable : public Ipv4RoutingProtocol -{ - std::map m_table; ///< Data structure for the routing table. - Ptr m_ipv4; - - Ipv4Address m_mainAddress; // used only for printing debug messages - - void DoDispose () - { - m_ipv4 = 0; - Ipv4RoutingProtocol::DoDispose (); - } - -public: - - RoutingTable () {} - - void SetIpv4 (Ptr ipv4); - void SetMainAddress (Ipv4Address mainAddress); - - ~RoutingTable () {} - - void Clear (); - uint32_t GetSize () const { return m_table.size (); } - std::vector GetEntries () const; - void RemoveEntry (const Ipv4Address &dest); - void AddEntry (const Ipv4Address &dest, - const Ipv4Address &next, - uint32_t interface, - uint32_t distance); - void AddEntry (const Ipv4Address &dest, - const Ipv4Address &next, - const Ipv4Address &interfaceAddress, - uint32_t distance); - bool Lookup (const Ipv4Address &dest, - RoutingTableEntry &outEntry) const; - bool FindSendEntry (const RoutingTableEntry &entry, - RoutingTableEntry &outEntry) const; - - // From Ipv4RoutingProtocol - virtual bool RequestRoute (uint32_t ifIndex, - const Ipv4Header &ipHeader, - Ptr packet, - RouteReplyCallback routeReply); - virtual bool RequestIfIndex (Ipv4Address destination, - uint32_t& ifIndex); - -}; - -}}; // namespace ns3, olsr - -#endif From 6c31e941d3f9d24c0b4198816651cc2c5ecfe6fe Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 23 Mar 2009 13:12:19 +0100 Subject: [PATCH 03/49] actually reset callbacks in the socket's DoDispose method --- src/node/socket.cc | 13 +++++++++++++ src/node/socket.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/node/socket.cc b/src/node/socket.cc index 4ceac3962..e13dcf43e 100644 --- a/src/node/socket.cc +++ b/src/node/socket.cc @@ -252,6 +252,19 @@ Socket::NotifyDataRecv (void) } } +void +Socket::DoDispose (void) +{ + + m_connectionSucceeded = MakeNullCallback > (); + m_connectionFailed = MakeNullCallback > (); + m_connectionRequest = MakeNullCallback, const Address &> (); + m_newConnectionCreated = MakeNullCallback, const Address &> (); + m_dataSent = MakeNullCallback, uint32_t> (); + m_sendCb = MakeNullCallback, uint32_t> (); + m_receivedData = MakeNullCallback > (); +} + /*************************************************************** * Socket Tags ***************************************************************/ diff --git a/src/node/socket.h b/src/node/socket.h index caba3627b..fbc4b723c 100644 --- a/src/node/socket.h +++ b/src/node/socket.h @@ -499,6 +499,7 @@ protected: void NotifyDataSent (uint32_t size); void NotifySend (uint32_t spaceAvailable); void NotifyDataRecv (void); + virtual void DoDispose (void); private: Callback > m_connectionSucceeded; Callback > m_connectionFailed; From 57e34c0323a9a6efef7b146efe4e1684ad5529ad Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 23 Mar 2009 13:21:51 +0100 Subject: [PATCH 04/49] good god, there should be no reason to call Dispose directly here. --- src/routing/olsr/olsr-agent-impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routing/olsr/olsr-agent-impl.cc b/src/routing/olsr/olsr-agent-impl.cc index 8096ce543..88a66d49d 100644 --- a/src/routing/olsr/olsr-agent-impl.cc +++ b/src/routing/olsr/olsr-agent-impl.cc @@ -218,7 +218,7 @@ void AgentImpl::DoDispose () for (std::map< Ptr, Ipv4Address >::iterator iter = m_socketAddresses.begin (); iter != m_socketAddresses.end (); iter++) { - iter->first->Dispose (); + iter->first->Close (); } m_socketAddresses.clear (); From 97b726344f1dec0e968c6eac8f300297427ff2e8 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 23 Mar 2009 14:37:43 +0100 Subject: [PATCH 05/49] rename --- bindings/python/ns3_module_olsr.py | 98 ++++----- src/helper/olsr-helper.cc | 10 +- ...agent-impl.cc => olsr-routing-protocol.cc} | 186 +++++++++--------- ...r-agent-impl.h => olsr-routing-protocol.h} | 6 +- src/routing/olsr/wscript | 4 +- utils/print-introspected-doxygen.cc | 2 +- 6 files changed, 153 insertions(+), 153 deletions(-) rename src/routing/olsr/{olsr-agent-impl.cc => olsr-routing-protocol.cc} (93%) rename src/routing/olsr/{olsr-agent-impl.h => olsr-routing-protocol.h} (98%) diff --git a/bindings/python/ns3_module_olsr.py b/bindings/python/ns3_module_olsr.py index 5e5f2e2a3..7e7b633cd 100644 --- a/bindings/python/ns3_module_olsr.py +++ b/bindings/python/ns3_module_olsr.py @@ -51,8 +51,6 @@ def register_types_ns3_internal(module): def register_types_ns3_olsr(module): root_module = module.get_root() - ## olsr-agent-impl.h: ns3::olsr::AgentImpl [class] - module.add_class('AgentImpl', parent=root_module['ns3::Ipv4RoutingProtocol']) ## olsr-repositories.h: ns3::olsr::DuplicateTuple [struct] module.add_class('DuplicateTuple') ## olsr-repositories.h: ns3::olsr::IfaceAssocTuple [struct] @@ -83,7 +81,9 @@ def register_types_ns3_olsr(module): module.add_enum('Status', ['STATUS_NOT_SYM', 'STATUS_SYM'], outer_class=root_module['ns3::olsr::NeighborTuple']) ## olsr-header.h: ns3::olsr::PacketHeader [class] module.add_class('PacketHeader', parent=root_module['ns3::Header']) - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry [struct] + ## olsr-routing-protocol.h: ns3::olsr::RoutingProtocol [class] + module.add_class('RoutingProtocol', parent=root_module['ns3::Ipv4RoutingProtocol']) + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry [struct] module.add_class('RoutingTableEntry') ## olsr-repositories.h: ns3::olsr::TopologyTuple [struct] module.add_class('TopologyTuple') @@ -94,7 +94,6 @@ def register_types_ns3_olsr(module): def register_methods(root_module): register_Ns3OlsrState_methods(root_module, root_module['ns3::OlsrState']) - register_Ns3OlsrAgentImpl_methods(root_module, root_module['ns3::olsr::AgentImpl']) register_Ns3OlsrDuplicateTuple_methods(root_module, root_module['ns3::olsr::DuplicateTuple']) register_Ns3OlsrIfaceAssocTuple_methods(root_module, root_module['ns3::olsr::IfaceAssocTuple']) register_Ns3OlsrLinkTuple_methods(root_module, root_module['ns3::olsr::LinkTuple']) @@ -108,6 +107,7 @@ def register_methods(root_module): register_Ns3OlsrMprSelectorTuple_methods(root_module, root_module['ns3::olsr::MprSelectorTuple']) register_Ns3OlsrNeighborTuple_methods(root_module, root_module['ns3::olsr::NeighborTuple']) register_Ns3OlsrPacketHeader_methods(root_module, root_module['ns3::olsr::PacketHeader']) + register_Ns3OlsrRoutingProtocol_methods(root_module, root_module['ns3::olsr::RoutingProtocol']) register_Ns3OlsrRoutingTableEntry_methods(root_module, root_module['ns3::olsr::RoutingTableEntry']) register_Ns3OlsrTopologyTuple_methods(root_module, root_module['ns3::olsr::TopologyTuple']) register_Ns3OlsrTwoHopNeighborTuple_methods(root_module, root_module['ns3::olsr::TwoHopNeighborTuple']) @@ -306,45 +306,6 @@ def register_Ns3OlsrState_methods(root_module, cls): is_const=True) return -def register_Ns3OlsrAgentImpl_methods(root_module, cls): - ## olsr-agent-impl.h: ns3::olsr::AgentImpl::AgentImpl(ns3::olsr::AgentImpl const & arg0) [copy constructor] - cls.add_constructor([param('ns3::olsr::AgentImpl const &', 'arg0')]) - ## olsr-agent-impl.h: static ns3::TypeId ns3::olsr::AgentImpl::GetTypeId() [member function] - cls.add_method('GetTypeId', - 'ns3::TypeId', - [], - is_static=True) - ## olsr-agent-impl.h: ns3::olsr::AgentImpl::AgentImpl() [constructor] - cls.add_constructor([]) - ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::SetNode(ns3::Ptr node) [member function] - cls.add_method('SetNode', - 'void', - [param('ns3::Ptr< ns3::Node >', 'node')]) - ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::Start() [member function] - cls.add_method('Start', - 'void', - []) - ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::SetMainInterface(uint32_t interface) [member function] - cls.add_method('SetMainInterface', - 'void', - [param('uint32_t', 'interface')]) - ## olsr-agent-impl.h: bool ns3::olsr::AgentImpl::RequestRoute(uint32_t ifIndex, ns3::Ipv4Header const & ipHeader, ns3::Ptr packet, ns3::Callback,const ns3::Ipv4Header&,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> routeReply) [member function] - cls.add_method('RequestRoute', - 'bool', - [param('uint32_t', 'ifIndex'), param('ns3::Ipv4Header const &', 'ipHeader'), param('ns3::Ptr< ns3::Packet >', 'packet'), param('ns3::Callback< void, bool, ns3::Ipv4Route const &, ns3::Ptr< ns3::Packet >, ns3::Ipv4Header const &, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'routeReply')], - visibility='private', is_virtual=True) - ## olsr-agent-impl.h: bool ns3::olsr::AgentImpl::RequestIfIndex(ns3::Ipv4Address destination, uint32_t & ifIndex) [member function] - cls.add_method('RequestIfIndex', - 'bool', - [param('ns3::Ipv4Address', 'destination'), param('uint32_t &', 'ifIndex')], - visibility='private', is_virtual=True) - ## olsr-agent-impl.h: void ns3::olsr::AgentImpl::DoDispose() [member function] - cls.add_method('DoDispose', - 'void', - [], - visibility='private', is_virtual=True) - return - def register_Ns3OlsrDuplicateTuple_methods(root_module, cls): cls.add_binary_comparison_operator('==') ## olsr-repositories.h: ns3::olsr::DuplicateTuple::DuplicateTuple() [constructor] @@ -757,18 +718,57 @@ def register_Ns3OlsrPacketHeader_methods(root_module, cls): [param('uint16_t', 'seqnum')]) return +def register_Ns3OlsrRoutingProtocol_methods(root_module, cls): + ## olsr-routing-protocol.h: ns3::olsr::RoutingProtocol::RoutingProtocol(ns3::olsr::RoutingProtocol const & arg0) [copy constructor] + cls.add_constructor([param('ns3::olsr::RoutingProtocol const &', 'arg0')]) + ## olsr-routing-protocol.h: static ns3::TypeId ns3::olsr::RoutingProtocol::GetTypeId() [member function] + cls.add_method('GetTypeId', + 'ns3::TypeId', + [], + is_static=True) + ## olsr-routing-protocol.h: ns3::olsr::RoutingProtocol::RoutingProtocol() [constructor] + cls.add_constructor([]) + ## olsr-routing-protocol.h: void ns3::olsr::RoutingProtocol::SetNode(ns3::Ptr node) [member function] + cls.add_method('SetNode', + 'void', + [param('ns3::Ptr< ns3::Node >', 'node')]) + ## olsr-routing-protocol.h: void ns3::olsr::RoutingProtocol::Start() [member function] + cls.add_method('Start', + 'void', + []) + ## olsr-routing-protocol.h: void ns3::olsr::RoutingProtocol::SetMainInterface(uint32_t interface) [member function] + cls.add_method('SetMainInterface', + 'void', + [param('uint32_t', 'interface')]) + ## olsr-routing-protocol.h: bool ns3::olsr::RoutingProtocol::RequestRoute(uint32_t ifIndex, ns3::Ipv4Header const & ipHeader, ns3::Ptr packet, ns3::Callback,const ns3::Ipv4Header&,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> routeReply) [member function] + cls.add_method('RequestRoute', + 'bool', + [param('uint32_t', 'ifIndex'), param('ns3::Ipv4Header const &', 'ipHeader'), param('ns3::Ptr< ns3::Packet >', 'packet'), param('ns3::Callback< void, bool, ns3::Ipv4Route const &, ns3::Ptr< ns3::Packet >, ns3::Ipv4Header const &, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'routeReply')], + visibility='private', is_virtual=True) + ## olsr-routing-protocol.h: bool ns3::olsr::RoutingProtocol::RequestIfIndex(ns3::Ipv4Address destination, uint32_t & ifIndex) [member function] + cls.add_method('RequestIfIndex', + 'bool', + [param('ns3::Ipv4Address', 'destination'), param('uint32_t &', 'ifIndex')], + visibility='private', is_virtual=True) + ## olsr-routing-protocol.h: void ns3::olsr::RoutingProtocol::DoDispose() [member function] + cls.add_method('DoDispose', + 'void', + [], + visibility='private', is_virtual=True) + return + def register_Ns3OlsrRoutingTableEntry_methods(root_module, cls): - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::destAddr [variable] + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry::destAddr [variable] cls.add_instance_attribute('destAddr', 'ns3::Ipv4Address', is_const=False) - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::nextAddr [variable] + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry::nextAddr [variable] cls.add_instance_attribute('nextAddr', 'ns3::Ipv4Address', is_const=False) - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::interface [variable] + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry::interface [variable] cls.add_instance_attribute('interface', 'uint32_t', is_const=False) - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::distance [variable] + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry::distance [variable] cls.add_instance_attribute('distance', 'uint32_t', is_const=False) - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry(ns3::olsr::RoutingTableEntry const & arg0) [copy constructor] + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry(ns3::olsr::RoutingTableEntry const & arg0) [copy constructor] cls.add_constructor([param('ns3::olsr::RoutingTableEntry const &', 'arg0')]) - ## olsr-agent-impl.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry() [constructor] + ## olsr-routing-protocol.h: ns3::olsr::RoutingTableEntry::RoutingTableEntry() [constructor] cls.add_constructor([]) return diff --git a/src/helper/olsr-helper.cc b/src/helper/olsr-helper.cc index 98a735be5..58526d33a 100644 --- a/src/helper/olsr-helper.cc +++ b/src/helper/olsr-helper.cc @@ -18,7 +18,7 @@ * Author: Mathieu Lacage */ #include "olsr-helper.h" -#include "ns3/olsr-agent-impl.h" +#include "ns3/olsr-routing-protocol.h" #include "ns3/node-list.h" #include "ns3/names.h" @@ -26,7 +26,7 @@ namespace ns3 { OlsrHelper::OlsrHelper () { - m_agentFactory.SetTypeId ("ns3::olsr::AgentImpl"); + m_agentFactory.SetTypeId ("ns3::olsr::RoutingProtocol"); } void @@ -63,13 +63,13 @@ OlsrHelper::Install (NodeContainer container) void OlsrHelper::Install (Ptr node) { - if (node->GetObject () != 0) + if (node->GetObject () != 0) { NS_FATAL_ERROR ("OlsrHelper::Install(): Aggregating " - "an Olsr Agent to a node with an existing Olsr Agent"); + "an Olsr Agent to a node with an existing Olsr RoutingProtocol"); return; } - Ptr agent = m_agentFactory.Create (); + Ptr agent = m_agentFactory.Create (); node->AggregateObject (agent); Ptr ipv4 = node->GetObject (); ipv4->AddRoutingProtocol (agent, 10); diff --git a/src/routing/olsr/olsr-agent-impl.cc b/src/routing/olsr/olsr-routing-protocol.cc similarity index 93% rename from src/routing/olsr/olsr-agent-impl.cc rename to src/routing/olsr/olsr-routing-protocol.cc index 88a66d49d..72c734008 100644 --- a/src/routing/olsr/olsr-agent-impl.cc +++ b/src/routing/olsr/olsr-routing-protocol.cc @@ -33,7 +33,7 @@ if (GetObject ()) { std::clog << "[node " << GetObject ()->GetId () << "] "; } -#include "olsr-agent-impl.h" +#include "olsr-routing-protocol.h" #include "ns3/socket-factory.h" #include "ns3/udp-socket-factory.h" #include "ns3/simulator.h" @@ -143,63 +143,63 @@ NS_LOG_COMPONENT_DEFINE ("OlsrAgent"); /********** OLSR class **********/ -NS_OBJECT_ENSURE_REGISTERED (AgentImpl); +NS_OBJECT_ENSURE_REGISTERED (RoutingProtocol); TypeId -AgentImpl::GetTypeId (void) +RoutingProtocol::GetTypeId (void) { - static TypeId tid = TypeId ("ns3::olsr::AgentImpl") + static TypeId tid = TypeId ("ns3::olsr::RoutingProtocol") .SetParent () - .AddConstructor () + .AddConstructor () .AddAttribute ("HelloInterval", "HELLO messages emission interval.", TimeValue (Seconds (2)), - MakeTimeAccessor (&AgentImpl::m_helloInterval), + MakeTimeAccessor (&RoutingProtocol::m_helloInterval), MakeTimeChecker ()) .AddAttribute ("TcInterval", "TC messages emission interval.", TimeValue (Seconds (5)), - MakeTimeAccessor (&AgentImpl::m_tcInterval), + MakeTimeAccessor (&RoutingProtocol::m_tcInterval), MakeTimeChecker ()) .AddAttribute ("MidInterval", "MID messages emission interval. Normally it is equal to TcInterval.", TimeValue (Seconds (5)), - MakeTimeAccessor (&AgentImpl::m_midInterval), + MakeTimeAccessor (&RoutingProtocol::m_midInterval), MakeTimeChecker ()) .AddAttribute ("Willingness", "Willingness of a node to carry and forward traffic for other nodes.", EnumValue (OLSR_WILL_DEFAULT), - MakeEnumAccessor (&AgentImpl::m_willingness), + MakeEnumAccessor (&RoutingProtocol::m_willingness), MakeEnumChecker (OLSR_WILL_NEVER, "never", OLSR_WILL_LOW, "low", OLSR_WILL_DEFAULT, "default", OLSR_WILL_HIGH, "high", OLSR_WILL_ALWAYS, "always")) .AddTraceSource ("Rx", "Receive OLSR packet.", - MakeTraceSourceAccessor (&AgentImpl::m_rxPacketTrace)) + MakeTraceSourceAccessor (&RoutingProtocol::m_rxPacketTrace)) .AddTraceSource ("Tx", "Send OLSR packet.", - MakeTraceSourceAccessor (&AgentImpl::m_txPacketTrace)) + MakeTraceSourceAccessor (&RoutingProtocol::m_txPacketTrace)) .AddTraceSource ("RoutingTableChanged", "The OLSR routing table has changed.", - MakeTraceSourceAccessor (&AgentImpl::m_routingTableChanged)) + MakeTraceSourceAccessor (&RoutingProtocol::m_routingTableChanged)) ; return tid; } -AgentImpl::AgentImpl () +RoutingProtocol::RoutingProtocol () : m_helloTimer (Timer::CANCEL_ON_DESTROY), m_tcTimer (Timer::CANCEL_ON_DESTROY), m_midTimer (Timer::CANCEL_ON_DESTROY) {} -AgentImpl::~AgentImpl () +RoutingProtocol::~RoutingProtocol () {} void -AgentImpl::SetNode (Ptr node) +RoutingProtocol::SetNode (Ptr node) { - NS_LOG_DEBUG ("Created olsr::AgentImpl"); - m_helloTimer.SetFunction (&AgentImpl::HelloTimerExpire, this); - m_tcTimer.SetFunction (&AgentImpl::TcTimerExpire, this); - m_midTimer.SetFunction (&AgentImpl::MidTimerExpire, this); - m_queuedMessagesTimer.SetFunction (&AgentImpl::SendQueuedMessages, this); + NS_LOG_DEBUG ("Created olsr::RoutingProtocol"); + m_helloTimer.SetFunction (&RoutingProtocol::HelloTimerExpire, this); + m_tcTimer.SetFunction (&RoutingProtocol::TcTimerExpire, this); + m_midTimer.SetFunction (&RoutingProtocol::MidTimerExpire, this); + m_queuedMessagesTimer.SetFunction (&RoutingProtocol::SendQueuedMessages, this); m_packetSequenceNumber = OLSR_MAX_SEQ_NUM; m_messageSequenceNumber = OLSR_MAX_SEQ_NUM; @@ -211,7 +211,7 @@ AgentImpl::SetNode (Ptr node) NS_ASSERT (m_ipv4); } -void AgentImpl::DoDispose () +void RoutingProtocol::DoDispose () { m_ipv4 = 0; @@ -225,7 +225,7 @@ void AgentImpl::DoDispose () Ipv4RoutingProtocol::DoDispose (); } -void AgentImpl::Start () +void RoutingProtocol::Start () { if (m_mainAddress == Ipv4Address ()) { @@ -267,7 +267,7 @@ void AgentImpl::Start () // Create a socket to listen only on this interface Ptr socket = Socket::CreateSocket (GetObject (), UdpSocketFactory::GetTypeId()); - socket->SetRecvCallback (MakeCallback (&AgentImpl::RecvOlsr, this)); + socket->SetRecvCallback (MakeCallback (&RoutingProtocol::RecvOlsr, this)); if (socket->Bind (InetSocketAddress (addr, OLSR_PORT_NUMBER))) { NS_FATAL_ERROR ("Failed to bind() OLSR receive socket"); @@ -283,7 +283,7 @@ void AgentImpl::Start () NS_LOG_DEBUG ("OLSR on node " << m_mainAddress << " started"); } -void AgentImpl::SetMainInterface (uint32_t interface) +void RoutingProtocol::SetMainInterface (uint32_t interface) { m_mainAddress = m_ipv4->GetAddress (interface); } @@ -292,7 +292,7 @@ void AgentImpl::SetMainInterface (uint32_t interface) // // \brief Processes an incoming %OLSR packet following RFC 3626 specification. void -AgentImpl::RecvOlsr (Ptr socket) +RoutingProtocol::RecvOlsr (Ptr socket) { Ptr receivedPacket; Address sourceAddress; @@ -441,7 +441,7 @@ AgentImpl::RecvOlsr (Ptr socket) /// \return the degree of the node. /// int -AgentImpl::Degree (NeighborTuple const &tuple) +RoutingProtocol::Degree (NeighborTuple const &tuple) { int degree = 0; for (TwoHopNeighborSet::const_iterator it = m_state.GetTwoHopNeighbors ().begin (); @@ -463,7 +463,7 @@ AgentImpl::Degree (NeighborTuple const &tuple) /// \brief Computates MPR set of a node following RFC 3626 hints. /// void -AgentImpl::MprComputation() +RoutingProtocol::MprComputation() { NS_LOG_FUNCTION (this); @@ -732,7 +732,7 @@ AgentImpl::MprComputation() /// \return the corresponding main address. /// Ipv4Address -AgentImpl::GetMainAddress (Ipv4Address iface_addr) const +RoutingProtocol::GetMainAddress (Ipv4Address iface_addr) const { const IfaceAssocTuple *tuple = m_state.FindIfaceAssocTuple (iface_addr); @@ -747,7 +747,7 @@ AgentImpl::GetMainAddress (Ipv4Address iface_addr) const /// \brief Creates the routing table of the node following RFC 3626 hints. /// void -AgentImpl::RoutingTableComputation () +RoutingProtocol::RoutingTableComputation () { NS_LOG_DEBUG (Simulator::Now ().GetSeconds () << " s: Node " << m_mainAddress << ": RoutingTableComputation begin..."); @@ -994,7 +994,7 @@ AgentImpl::RoutingTableComputation () /// \param sender_iface the address of the interface where the message was sent from. /// void -AgentImpl::ProcessHello (const olsr::MessageHeader &msg, +RoutingProtocol::ProcessHello (const olsr::MessageHeader &msg, const Ipv4Address &receiverIface, const Ipv4Address &senderIface) { @@ -1055,7 +1055,7 @@ AgentImpl::ProcessHello (const olsr::MessageHeader &msg, /// \param sender_iface the address of the interface where the message was sent from. /// void -AgentImpl::ProcessTc (const olsr::MessageHeader &msg, +RoutingProtocol::ProcessTc (const olsr::MessageHeader &msg, const Ipv4Address &senderIface) { const olsr::MessageHeader::Tc &tc = msg.GetTc (); @@ -1118,7 +1118,7 @@ AgentImpl::ProcessTc (const olsr::MessageHeader &msg, // Schedules topology tuple deletion m_events.Track (Simulator::Schedule (DELAY (topologyTuple.expirationTime), - &AgentImpl::TopologyTupleTimerExpire, + &RoutingProtocol::TopologyTupleTimerExpire, this, topologyTuple.destAddr, topologyTuple.lastAddr)); @@ -1150,7 +1150,7 @@ AgentImpl::ProcessTc (const olsr::MessageHeader &msg, /// \param sender_iface the address of the interface where the message was sent from. /// void -AgentImpl::ProcessMid (const olsr::MessageHeader &msg, +RoutingProtocol::ProcessMid (const olsr::MessageHeader &msg, const Ipv4Address &senderIface) { const olsr::MessageHeader::Mid &mid = msg.GetMid (); @@ -1196,7 +1196,7 @@ AgentImpl::ProcessMid (const olsr::MessageHeader &msg, NS_LOG_LOGIC ("New IfaceAssoc added: " << tuple); // Schedules iface association tuple deletion Simulator::Schedule (DELAY (tuple.time), - &AgentImpl::IfaceAssocTupleTimerExpire, this, tuple.ifaceAddr); + &RoutingProtocol::IfaceAssocTupleTimerExpire, this, tuple.ifaceAddr); } } @@ -1232,7 +1232,7 @@ AgentImpl::ProcessMid (const olsr::MessageHeader &msg, /// \param local_iface the address of the interface where the message was received from. /// void -AgentImpl::ForwardDefault (olsr::MessageHeader olsrMessage, +RoutingProtocol::ForwardDefault (olsr::MessageHeader olsrMessage, DuplicateTuple *duplicated, const Ipv4Address &localIface, const Ipv4Address &senderAddress) @@ -1292,7 +1292,7 @@ AgentImpl::ForwardDefault (olsr::MessageHeader olsrMessage, AddDuplicateTuple (newDup); // Schedule dup tuple deletion Simulator::Schedule (OLSR_DUP_HOLD_TIME, - &AgentImpl::DupTupleTimerExpire, this, + &RoutingProtocol::DupTupleTimerExpire, this, newDup.address, newDup.sequenceNumber); } } @@ -1307,7 +1307,7 @@ AgentImpl::ForwardDefault (olsr::MessageHeader olsrMessage, /// \param delay maximum delay the %OLSR message is going to be buffered. /// void -AgentImpl::QueueMessage (const olsr::MessageHeader &message, Time delay) +RoutingProtocol::QueueMessage (const olsr::MessageHeader &message, Time delay) { m_queuedMessages.push_back (message); if (not m_queuedMessagesTimer.IsRunning ()) @@ -1318,7 +1318,7 @@ AgentImpl::QueueMessage (const olsr::MessageHeader &message, Time delay) } void -AgentImpl::SendPacket (Ptr packet, +RoutingProtocol::SendPacket (Ptr packet, const MessageList &containedMessages) { NS_LOG_DEBUG ("OLSR node " << m_mainAddress << " sending a OLSR packet"); @@ -1344,7 +1344,7 @@ AgentImpl::SendPacket (Ptr packet, /// dictated by OLSR_MAX_MSGS constant. /// void -AgentImpl::SendQueuedMessages () +RoutingProtocol::SendQueuedMessages () { Ptr packet = Create (); int numMessages = 0; @@ -1383,7 +1383,7 @@ AgentImpl::SendQueuedMessages () /// \brief Creates a new %OLSR HELLO message which is buffered for being sent later on. /// void -AgentImpl::SendHello () +RoutingProtocol::SendHello () { NS_LOG_FUNCTION (this); @@ -1494,7 +1494,7 @@ AgentImpl::SendHello () /// \brief Creates a new %OLSR TC message which is buffered for being sent later on. /// void -AgentImpl::SendTc () +RoutingProtocol::SendTc () { NS_LOG_FUNCTION (this); @@ -1520,7 +1520,7 @@ AgentImpl::SendTc () /// \brief Creates a new %OLSR MID message which is buffered for being sent later on. /// void -AgentImpl::SendMid () +RoutingProtocol::SendMid () { olsr::MessageHeader msg; olsr::MessageHeader::Mid &mid = msg.GetMid (); @@ -1564,7 +1564,7 @@ AgentImpl::SendMid () /// \brief Updates Link Set according to a new received HELLO message (following RFC 3626 /// specification). Neighbor Set is also updated if needed. void -AgentImpl::LinkSensing (const olsr::MessageHeader &msg, +RoutingProtocol::LinkSensing (const olsr::MessageHeader &msg, const olsr::MessageHeader::Hello &hello, const Ipv4Address &receiverIface, const Ipv4Address &senderIface) @@ -1688,7 +1688,7 @@ AgentImpl::LinkSensing (const olsr::MessageHeader &msg, { LinkTupleAdded (*link_tuple, hello.willingness); m_events.Track (Simulator::Schedule (DELAY (std::min (link_tuple->time, link_tuple->symTime)), - &AgentImpl::LinkTupleTimerExpire, this, + &RoutingProtocol::LinkTupleTimerExpire, this, link_tuple->neighborIfaceAddr)); } NS_LOG_DEBUG ("@" << now.GetSeconds () << ": Olsr node " << m_mainAddress @@ -1699,7 +1699,7 @@ AgentImpl::LinkSensing (const olsr::MessageHeader &msg, /// \brief Updates the Neighbor Set according to the information contained in a new received /// HELLO message (following RFC 3626). void -AgentImpl::PopulateNeighborSet (const olsr::MessageHeader &msg, +RoutingProtocol::PopulateNeighborSet (const olsr::MessageHeader &msg, const olsr::MessageHeader::Hello &hello) { NeighborTuple *nb_tuple = m_state.FindNeighborTuple (msg.GetOriginatorAddress ()); @@ -1714,7 +1714,7 @@ AgentImpl::PopulateNeighborSet (const olsr::MessageHeader &msg, /// \brief Updates the 2-hop Neighbor Set according to the information contained in a new /// received HELLO message (following RFC 3626). void -AgentImpl::PopulateTwoHopNeighborSet (const olsr::MessageHeader &msg, +RoutingProtocol::PopulateTwoHopNeighborSet (const olsr::MessageHeader &msg, const olsr::MessageHeader::Hello &hello) { Time now = Simulator::Now (); @@ -1789,7 +1789,7 @@ AgentImpl::PopulateTwoHopNeighborSet (const olsr::MessageHeader &msg, AddTwoHopNeighborTuple (new_nb2hop_tuple); // Schedules nb2hop tuple deletion m_events.Track (Simulator::Schedule (DELAY (new_nb2hop_tuple.expirationTime), - &AgentImpl::Nb2hopTupleTimerExpire, this, + &RoutingProtocol::Nb2hopTupleTimerExpire, this, new_nb2hop_tuple.neighborMainAddr, new_nb2hop_tuple.twoHopNeighborAddr)); } @@ -1826,7 +1826,7 @@ AgentImpl::PopulateTwoHopNeighborSet (const olsr::MessageHeader &msg, /// \brief Updates the MPR Selector Set according to the information contained in a new /// received HELLO message (following RFC 3626). void -AgentImpl::PopulateMprSelectorSet (const olsr::MessageHeader &msg, +RoutingProtocol::PopulateMprSelectorSet (const olsr::MessageHeader &msg, const olsr::MessageHeader::Hello &hello) { NS_LOG_FUNCTION (this); @@ -1866,7 +1866,7 @@ AgentImpl::PopulateMprSelectorSet (const olsr::MessageHeader &msg, // Schedules mpr selector tuple deletion m_events.Track (Simulator::Schedule (DELAY (mprsel_tuple.expirationTime), - &AgentImpl::MprSelTupleTimerExpire, this, + &RoutingProtocol::MprSelTupleTimerExpire, this, mprsel_tuple.mainAddr)); } else @@ -1926,7 +1926,7 @@ OLSR::mac_failed(Ptr p) { /// \param tuple link tuple with the information of the link to the neighbor which has been lost. /// void -AgentImpl::NeighborLoss (const LinkTuple &tuple) +RoutingProtocol::NeighborLoss (const LinkTuple &tuple) { NS_LOG_DEBUG (Simulator::Now ().GetSeconds () << "s: OLSR Node " << m_mainAddress @@ -1945,7 +1945,7 @@ AgentImpl::NeighborLoss (const LinkTuple &tuple) /// \param tuple the duplicate tuple to be added. /// void -AgentImpl::AddDuplicateTuple (const DuplicateTuple &tuple) +RoutingProtocol::AddDuplicateTuple (const DuplicateTuple &tuple) { /*debug("%f: Node %d adds dup tuple: addr = %d seq_num = %d\n", Simulator::Now (), @@ -1961,7 +1961,7 @@ AgentImpl::AddDuplicateTuple (const DuplicateTuple &tuple) /// \param tuple the duplicate tuple to be removed. /// void -AgentImpl::RemoveDuplicateTuple (const DuplicateTuple &tuple) +RoutingProtocol::RemoveDuplicateTuple (const DuplicateTuple &tuple) { /*debug("%f: Node %d removes dup tuple: addr = %d seq_num = %d\n", Simulator::Now (), @@ -1972,7 +1972,7 @@ AgentImpl::RemoveDuplicateTuple (const DuplicateTuple &tuple) } void -AgentImpl::LinkTupleAdded (const LinkTuple &tuple, uint8_t willingness) +RoutingProtocol::LinkTupleAdded (const LinkTuple &tuple, uint8_t willingness) { // Creates associated neighbor tuple NeighborTuple nb_tuple; @@ -1997,7 +1997,7 @@ AgentImpl::LinkTupleAdded (const LinkTuple &tuple, uint8_t willingness) /// \param tuple the link tuple to be removed. /// void -AgentImpl::RemoveLinkTuple (const LinkTuple &tuple) +RoutingProtocol::RemoveLinkTuple (const LinkTuple &tuple) { NS_LOG_DEBUG (Simulator::Now ().GetSeconds () << "s: OLSR Node " << m_mainAddress @@ -2015,7 +2015,7 @@ AgentImpl::RemoveLinkTuple (const LinkTuple &tuple) /// \param tuple the link tuple which has been updated. /// void -AgentImpl::LinkTupleUpdated (const LinkTuple &tuple, uint8_t willingness) +RoutingProtocol::LinkTupleUpdated (const LinkTuple &tuple, uint8_t willingness) { // Each time a link tuple changes, the associated neighbor tuple must be recomputed @@ -2062,7 +2062,7 @@ AgentImpl::LinkTupleUpdated (const LinkTuple &tuple, uint8_t willingness) /// \param tuple the neighbor tuple to be added. /// void -AgentImpl::AddNeighborTuple (const NeighborTuple &tuple) +RoutingProtocol::AddNeighborTuple (const NeighborTuple &tuple) { // debug("%f: Node %d adds neighbor tuple: nb_addr = %d status = %s\n", // Simulator::Now (), @@ -2080,7 +2080,7 @@ AgentImpl::AddNeighborTuple (const NeighborTuple &tuple) /// \param tuple the neighbor tuple to be removed. /// void -AgentImpl::RemoveNeighborTuple (const NeighborTuple &tuple) +RoutingProtocol::RemoveNeighborTuple (const NeighborTuple &tuple) { // debug("%f: Node %d removes neighbor tuple: nb_addr = %d status = %s\n", // Simulator::Now (), @@ -2098,7 +2098,7 @@ AgentImpl::RemoveNeighborTuple (const NeighborTuple &tuple) /// \param tuple the 2-hop neighbor tuple to be added. /// void -AgentImpl::AddTwoHopNeighborTuple (const TwoHopNeighborTuple &tuple) +RoutingProtocol::AddTwoHopNeighborTuple (const TwoHopNeighborTuple &tuple) { // debug("%f: Node %d adds 2-hop neighbor tuple: nb_addr = %d nb2hop_addr = %d\n", // Simulator::Now (), @@ -2115,7 +2115,7 @@ AgentImpl::AddTwoHopNeighborTuple (const TwoHopNeighborTuple &tuple) /// \param tuple the 2-hop neighbor tuple to be removed. /// void -AgentImpl::RemoveTwoHopNeighborTuple (const TwoHopNeighborTuple &tuple) +RoutingProtocol::RemoveTwoHopNeighborTuple (const TwoHopNeighborTuple &tuple) { // debug("%f: Node %d removes 2-hop neighbor tuple: nb_addr = %d nb2hop_addr = %d\n", // Simulator::Now (), @@ -2127,7 +2127,7 @@ AgentImpl::RemoveTwoHopNeighborTuple (const TwoHopNeighborTuple &tuple) } void -AgentImpl::IncrementAnsn () +RoutingProtocol::IncrementAnsn () { m_ansn = (m_ansn + 1) % (OLSR_MAX_SEQ_NUM + 1); } @@ -2140,7 +2140,7 @@ AgentImpl::IncrementAnsn () /// \param tuple the MPR selector tuple to be added. /// void -AgentImpl::AddMprSelectorTuple (const MprSelectorTuple &tuple) +RoutingProtocol::AddMprSelectorTuple (const MprSelectorTuple &tuple) { // debug("%f: Node %d adds MPR selector tuple: nb_addr = %d\n", // Simulator::Now (), @@ -2159,7 +2159,7 @@ AgentImpl::AddMprSelectorTuple (const MprSelectorTuple &tuple) /// \param tuple the MPR selector tuple to be removed. /// void -AgentImpl::RemoveMprSelectorTuple (const MprSelectorTuple &tuple) +RoutingProtocol::RemoveMprSelectorTuple (const MprSelectorTuple &tuple) { // debug("%f: Node %d removes MPR selector tuple: nb_addr = %d\n", // Simulator::Now (), @@ -2176,7 +2176,7 @@ AgentImpl::RemoveMprSelectorTuple (const MprSelectorTuple &tuple) /// \param tuple the topology tuple to be added. /// void -AgentImpl::AddTopologyTuple (const TopologyTuple &tuple) +RoutingProtocol::AddTopologyTuple (const TopologyTuple &tuple) { // debug("%f: Node %d adds topology tuple: dest_addr = %d last_addr = %d seq = %d\n", // Simulator::Now (), @@ -2194,7 +2194,7 @@ AgentImpl::AddTopologyTuple (const TopologyTuple &tuple) /// \param tuple the topology tuple to be removed. /// void -AgentImpl::RemoveTopologyTuple (const TopologyTuple &tuple) +RoutingProtocol::RemoveTopologyTuple (const TopologyTuple &tuple) { // debug("%f: Node %d removes topology tuple: dest_addr = %d last_addr = %d seq = %d\n", // Simulator::Now (), @@ -2212,7 +2212,7 @@ AgentImpl::RemoveTopologyTuple (const TopologyTuple &tuple) /// \param tuple the interface association tuple to be added. /// void -AgentImpl::AddIfaceAssocTuple (const IfaceAssocTuple &tuple) +RoutingProtocol::AddIfaceAssocTuple (const IfaceAssocTuple &tuple) { // debug("%f: Node %d adds iface association tuple: main_addr = %d iface_addr = %d\n", // Simulator::Now (), @@ -2229,7 +2229,7 @@ AgentImpl::AddIfaceAssocTuple (const IfaceAssocTuple &tuple) /// \param tuple the interface association tuple to be removed. /// void -AgentImpl::RemoveIfaceAssocTuple (const IfaceAssocTuple &tuple) +RoutingProtocol::RemoveIfaceAssocTuple (const IfaceAssocTuple &tuple) { // debug("%f: Node %d removes iface association tuple: main_addr = %d iface_addr = %d\n", // Simulator::Now (), @@ -2241,14 +2241,14 @@ AgentImpl::RemoveIfaceAssocTuple (const IfaceAssocTuple &tuple) } -uint16_t AgentImpl::GetPacketSequenceNumber () +uint16_t RoutingProtocol::GetPacketSequenceNumber () { m_packetSequenceNumber = (m_packetSequenceNumber + 1) % (OLSR_MAX_SEQ_NUM + 1); return m_packetSequenceNumber; } /// Increments message sequence number and returns the new value. -uint16_t AgentImpl::GetMessageSequenceNumber () +uint16_t RoutingProtocol::GetMessageSequenceNumber () { m_messageSequenceNumber = (m_messageSequenceNumber + 1) % (OLSR_MAX_SEQ_NUM + 1); return m_messageSequenceNumber; @@ -2260,7 +2260,7 @@ uint16_t AgentImpl::GetMessageSequenceNumber () /// \param e The event which has expired. /// void -AgentImpl::HelloTimerExpire () +RoutingProtocol::HelloTimerExpire () { SendHello (); m_helloTimer.Schedule (m_helloInterval); @@ -2271,7 +2271,7 @@ AgentImpl::HelloTimerExpire () /// \param e The event which has expired. /// void -AgentImpl::TcTimerExpire () +RoutingProtocol::TcTimerExpire () { if (m_state.GetMprSelectors ().size () > 0) { @@ -2290,7 +2290,7 @@ AgentImpl::TcTimerExpire () /// \param e The event which has expired. /// void -AgentImpl::MidTimerExpire () +RoutingProtocol::MidTimerExpire () { SendMid (); m_midTimer.Schedule (m_midInterval); @@ -2304,7 +2304,7 @@ AgentImpl::MidTimerExpire () /// \param tuple The tuple which has expired. /// void -AgentImpl::DupTupleTimerExpire (Ipv4Address address, uint16_t sequenceNumber) +RoutingProtocol::DupTupleTimerExpire (Ipv4Address address, uint16_t sequenceNumber) { DuplicateTuple *tuple = m_state.FindDuplicateTuple (address, sequenceNumber); @@ -2319,7 +2319,7 @@ AgentImpl::DupTupleTimerExpire (Ipv4Address address, uint16_t sequenceNumber) else { m_events.Track (Simulator::Schedule (DELAY (tuple->expirationTime), - &AgentImpl::DupTupleTimerExpire, this, + &RoutingProtocol::DupTupleTimerExpire, this, address, sequenceNumber)); } } @@ -2336,7 +2336,7 @@ AgentImpl::DupTupleTimerExpire (Ipv4Address address, uint16_t sequenceNumber) /// \param e The event which has expired. /// void -AgentImpl::LinkTupleTimerExpire (Ipv4Address neighborIfaceAddr) +RoutingProtocol::LinkTupleTimerExpire (Ipv4Address neighborIfaceAddr) { Time now = Simulator::Now (); @@ -2358,13 +2358,13 @@ AgentImpl::LinkTupleTimerExpire (Ipv4Address neighborIfaceAddr) NeighborLoss (*tuple); m_events.Track (Simulator::Schedule (DELAY (tuple->time), - &AgentImpl::LinkTupleTimerExpire, this, + &RoutingProtocol::LinkTupleTimerExpire, this, neighborIfaceAddr)); } else { m_events.Track (Simulator::Schedule (DELAY (std::min (tuple->time, tuple->symTime)), - &AgentImpl::LinkTupleTimerExpire, this, + &RoutingProtocol::LinkTupleTimerExpire, this, neighborIfaceAddr)); } } @@ -2377,7 +2377,7 @@ AgentImpl::LinkTupleTimerExpire (Ipv4Address neighborIfaceAddr) /// \param e The event which has expired. /// void -AgentImpl::Nb2hopTupleTimerExpire (Ipv4Address neighborMainAddr, Ipv4Address twoHopNeighborAddr) +RoutingProtocol::Nb2hopTupleTimerExpire (Ipv4Address neighborMainAddr, Ipv4Address twoHopNeighborAddr) { TwoHopNeighborTuple *tuple; tuple = m_state.FindTwoHopNeighborTuple (neighborMainAddr, twoHopNeighborAddr); @@ -2392,7 +2392,7 @@ AgentImpl::Nb2hopTupleTimerExpire (Ipv4Address neighborMainAddr, Ipv4Address two else { m_events.Track (Simulator::Schedule (DELAY (tuple->expirationTime), - &AgentImpl::Nb2hopTupleTimerExpire, + &RoutingProtocol::Nb2hopTupleTimerExpire, this, neighborMainAddr, twoHopNeighborAddr)); } } @@ -2405,7 +2405,7 @@ AgentImpl::Nb2hopTupleTimerExpire (Ipv4Address neighborMainAddr, Ipv4Address two /// \param e The event which has expired. /// void -AgentImpl::MprSelTupleTimerExpire (Ipv4Address mainAddr) +RoutingProtocol::MprSelTupleTimerExpire (Ipv4Address mainAddr) { MprSelectorTuple *tuple = m_state.FindMprSelectorTuple (mainAddr); if (tuple == NULL) @@ -2419,7 +2419,7 @@ AgentImpl::MprSelTupleTimerExpire (Ipv4Address mainAddr) else { m_events.Track (Simulator::Schedule (DELAY (tuple->expirationTime), - &AgentImpl::MprSelTupleTimerExpire, + &RoutingProtocol::MprSelTupleTimerExpire, this, mainAddr)); } } @@ -2432,7 +2432,7 @@ AgentImpl::MprSelTupleTimerExpire (Ipv4Address mainAddr) /// \param e The event which has expired. /// void -AgentImpl::TopologyTupleTimerExpire (Ipv4Address destAddr, Ipv4Address lastAddr) +RoutingProtocol::TopologyTupleTimerExpire (Ipv4Address destAddr, Ipv4Address lastAddr) { TopologyTuple *tuple = m_state.FindTopologyTuple (destAddr, lastAddr); if (tuple == NULL) @@ -2446,7 +2446,7 @@ AgentImpl::TopologyTupleTimerExpire (Ipv4Address destAddr, Ipv4Address lastAddr) else { m_events.Track (Simulator::Schedule (DELAY (tuple->expirationTime), - &AgentImpl::TopologyTupleTimerExpire, + &RoutingProtocol::TopologyTupleTimerExpire, this, tuple->destAddr, tuple->lastAddr)); } } @@ -2457,7 +2457,7 @@ AgentImpl::TopologyTupleTimerExpire (Ipv4Address destAddr, Ipv4Address lastAddr) /// \param e The event which has expired. /// void -AgentImpl::IfaceAssocTupleTimerExpire (Ipv4Address ifaceAddr) +RoutingProtocol::IfaceAssocTupleTimerExpire (Ipv4Address ifaceAddr) { IfaceAssocTuple *tuple = m_state.FindIfaceAssocTuple (ifaceAddr); if (tuple == NULL) @@ -2471,7 +2471,7 @@ AgentImpl::IfaceAssocTupleTimerExpire (Ipv4Address ifaceAddr) else { m_events.Track (Simulator::Schedule (DELAY (tuple->time), - &AgentImpl::IfaceAssocTupleTimerExpire, + &RoutingProtocol::IfaceAssocTupleTimerExpire, this, ifaceAddr)); } } @@ -2480,7 +2480,7 @@ AgentImpl::IfaceAssocTupleTimerExpire (Ipv4Address ifaceAddr) /// \brief Clears the routing table and frees the memory assigned to each one of its entries. /// void -AgentImpl::Clear () +RoutingProtocol::Clear () { NS_LOG_FUNCTION_NOARGS (); m_table.clear (); @@ -2491,7 +2491,7 @@ AgentImpl::Clear () /// \param dest address of the destination node. /// void -AgentImpl::RemoveEntry (Ipv4Address const &dest) +RoutingProtocol::RemoveEntry (Ipv4Address const &dest) { m_table.erase (dest); } @@ -2503,7 +2503,7 @@ AgentImpl::RemoveEntry (Ipv4Address const &dest) /// \return true if found, false if not found /// bool -AgentImpl::Lookup (Ipv4Address const &dest, +RoutingProtocol::Lookup (Ipv4Address const &dest, RoutingTableEntry &outEntry) const { // Get the iterator at "dest" position @@ -2533,7 +2533,7 @@ AgentImpl::Lookup (Ipv4Address const &dest, /// if there is no such entry. /// bool -AgentImpl::FindSendEntry (RoutingTableEntry const &entry, +RoutingProtocol::FindSendEntry (RoutingTableEntry const &entry, RoutingTableEntry &outEntry) const { outEntry = entry; @@ -2547,7 +2547,7 @@ AgentImpl::FindSendEntry (RoutingTableEntry const &entry, bool -AgentImpl::RequestRoute (uint32_t ifIndex, +RoutingProtocol::RequestRoute (uint32_t ifIndex, const Ipv4Header &ipHeader, Ptr packet, RouteReplyCallback routeReply) @@ -2590,7 +2590,7 @@ AgentImpl::RequestRoute (uint32_t ifIndex, } bool -AgentImpl::RequestIfIndex (Ipv4Address destination, +RoutingProtocol::RequestIfIndex (Ipv4Address destination, uint32_t& ifIndex) { RoutingTableEntry entry1, entry2; @@ -2620,7 +2620,7 @@ AgentImpl::RequestIfIndex (Ipv4Address destination, /// \param dist distance to the destination node. /// void -AgentImpl::AddEntry (Ipv4Address const &dest, +RoutingProtocol::AddEntry (Ipv4Address const &dest, Ipv4Address const &next, uint32_t interface, uint32_t distance) @@ -2639,7 +2639,7 @@ AgentImpl::AddEntry (Ipv4Address const &dest, } void -AgentImpl::AddEntry (Ipv4Address const &dest, +RoutingProtocol::AddEntry (Ipv4Address const &dest, Ipv4Address const &next, Ipv4Address const &interfaceAddress, uint32_t distance) @@ -2664,7 +2664,7 @@ AgentImpl::AddEntry (Ipv4Address const &dest, std::vector -AgentImpl::GetEntries () const +RoutingProtocol::GetEntries () const { std::vector retval; for (std::map::const_iterator iter = m_table.begin (); diff --git a/src/routing/olsr/olsr-agent-impl.h b/src/routing/olsr/olsr-routing-protocol.h similarity index 98% rename from src/routing/olsr/olsr-agent-impl.h rename to src/routing/olsr/olsr-routing-protocol.h index e4433625c..cf7a606d1 100644 --- a/src/routing/olsr/olsr-agent-impl.h +++ b/src/routing/olsr/olsr-routing-protocol.h @@ -59,13 +59,13 @@ struct RoutingTableEntry }; -class AgentImpl : public Ipv4RoutingProtocol +class RoutingProtocol : public Ipv4RoutingProtocol { public: static TypeId GetTypeId (void); - AgentImpl (); - virtual ~AgentImpl (); + RoutingProtocol (); + virtual ~RoutingProtocol (); void SetNode (Ptr node); diff --git a/src/routing/olsr/wscript b/src/routing/olsr/wscript index b46ffbb9e..7170e4478 100644 --- a/src/routing/olsr/wscript +++ b/src/routing/olsr/wscript @@ -6,13 +6,13 @@ def build(bld): module.source = [ 'olsr-header.cc', 'olsr-state.cc', - 'olsr-agent-impl.cc', + 'olsr-routing-protocol.cc', ] headers = bld.new_task_gen('ns3header') headers.module = 'olsr' headers.source = [ - 'olsr-agent-impl.h', + 'olsr-routing-protocol.h', 'olsr-header.h', 'olsr-state.h', 'olsr-repositories.h', diff --git a/utils/print-introspected-doxygen.cc b/utils/print-introspected-doxygen.cc index 12684629a..a1c5e468e 100644 --- a/utils/print-introspected-doxygen.cc +++ b/utils/print-introspected-doxygen.cc @@ -239,7 +239,7 @@ int main (int argc, char *argv[]) info.RecordAggregationInfo ("ns3::Node", "ns3::TcpSocketFactory"); info.RecordAggregationInfo ("ns3::Node", "ns3::UdpSocketFactory"); info.RecordAggregationInfo ("ns3::Node", "ns3::PacketSocketFactory"); - info.RecordAggregationInfo ("ns3::Node", "ns3::olsr::Agent"); + info.RecordAggregationInfo ("ns3::Node", "ns3::olsr::RoutingProtocol"); info.RecordAggregationInfo ("ns3::Node", "ns3::MobilityModel"); info.RecordAggregationInfo ("ns3::Node", "ns3::Ipv4L3Protocol"); info.RecordAggregationInfo ("ns3::Node", "ns3::ArpL3Protocol"); From 3e22a2d98d7ea6c41e24453ebe66e3476320b15d Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 2 Apr 2009 13:16:13 +0100 Subject: [PATCH 06/49] Less error hiding anti-pattern when running regression tests. --- regression.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/regression.py b/regression.py index 161f8b79b..f64f261ba 100644 --- a/regression.py +++ b/regression.py @@ -3,7 +3,7 @@ import os import sys import shutil import pproc as subprocess -import urllib +import errno # WAF modules import Build @@ -118,7 +118,11 @@ class regression_test_task(Task.TaskBase): if Options.options.regression_generate: # clean the target dir - shutil.rmtree(reference_traces_path, ignore_errors=True) + try: + shutil.rmtree(trace_output_path) + except OSError, ex: + if ex.errno not in [errno.ENOENT]: + raise os.makedirs(reference_traces_path) result = self.run_reference_generate(reference_traces_path, program, arguments, is_pyscript) if result == 0: @@ -127,7 +131,11 @@ class regression_test_task(Task.TaskBase): print "GENERATE FAIL " + self.test_name else: # clean the target dir - shutil.rmtree(trace_output_path, ignore_errors=True) + try: + shutil.rmtree(trace_output_path) + except OSError, ex: + if ex.errno not in [errno.ENOENT]: + raise os.makedirs(trace_output_path) # run it result = self.run_reference_test(reference_traces_path, trace_output_path, program, arguments, is_pyscript) From 0b4406a00555ef34c4f8f7a7fcfc91fbdef5458a Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 3 Apr 2009 20:09:07 +0200 Subject: [PATCH 07/49] fix typo --- doc/tutorial/in-process/introduction.texi | 2 +- examples/csma-broadcast.cc | 6 ++++++ samples/main-simulator.cc | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/tutorial/in-process/introduction.texi b/doc/tutorial/in-process/introduction.texi index f57fa0236..17cb4d7c9 100644 --- a/doc/tutorial/in-process/introduction.texi +++ b/doc/tutorial/in-process/introduction.texi @@ -487,7 +487,7 @@ the following into your Linux shell (assuming you have installed Mercurial): cd mkdir repos cd !$ - hg clone http://code.nanam.org/ns-3-dev + hg clone http://code.nsnam.org/ns-3-dev @end verbatim As the hg command executes, you should see something like the following, diff --git a/examples/csma-broadcast.cc b/examples/csma-broadcast.cc index da5e669a6..82673d85d 100644 --- a/examples/csma-broadcast.cc +++ b/examples/csma-broadcast.cc @@ -36,6 +36,7 @@ #include "ns3/simulator-module.h" #include "ns3/node-module.h" #include "ns3/helper-module.h" +#include "ns3/contrib-module.h" using namespace ns3; @@ -56,6 +57,9 @@ main (int argc, char *argv[]) CommandLine cmd; cmd.Parse (argc, argv); + GtkConfigStore config; + config.ConfigureDefaults (); + NS_LOG_INFO ("Create nodes."); NodeContainer c; c.Create (3); @@ -114,6 +118,8 @@ main (int argc, char *argv[]) ascii.open ("csma-broadcast.tr"); CsmaHelper::EnableAsciiAll (ascii); + config.ConfigureAttributes (); + NS_LOG_INFO ("Run Simulation."); Simulator::Run (); Simulator::Destroy (); diff --git a/samples/main-simulator.cc b/samples/main-simulator.cc index 1f93d7634..793c6a9c6 100644 --- a/samples/main-simulator.cc +++ b/samples/main-simulator.cc @@ -1,6 +1,7 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ #include "ns3/simulator.h" #include "ns3/nstime.h" +#include "ns3/command-line.h" #include using namespace ns3; @@ -37,6 +38,9 @@ random_function (MyModel *model) int main (int argc, char *argv[]) { + CommandLine cmd; + cmd.Parse (argc, argv); + MyModel model; Simulator::Schedule (Seconds (10.0), &random_function, &model); From 1f612b1dc0a7c35ba94555ecf9ce45807cbe11cb Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Sat, 4 Apr 2009 13:48:13 -0700 Subject: [PATCH 08/49] fix csma-broadcast for regression script --- examples/csma-broadcast.cc | 14 ++++++++++++-- regression/tests/test-csma-broadcast.py | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/examples/csma-broadcast.cc b/examples/csma-broadcast.cc index 82673d85d..04be532aa 100644 --- a/examples/csma-broadcast.cc +++ b/examples/csma-broadcast.cc @@ -52,13 +52,20 @@ main (int argc, char *argv[]) #endif LogComponentEnable ("CsmaBroadcastExample", LOG_PREFIX_TIME); + // By default, this example will launch the GtkConfigStore + bool suppressGtkConfigStore = false; + // Allow the user to override any of the defaults and the above // Bind()s at run-time, via command-line arguments CommandLine cmd; + cmd.AddValue ("suppressGtkConfigStore", "suppress GtkConfigStore if true", suppressGtkConfigStore); cmd.Parse (argc, argv); GtkConfigStore config; - config.ConfigureDefaults (); + if (suppressGtkConfigStore == false) + { + config.ConfigureDefaults (); + } NS_LOG_INFO ("Create nodes."); NodeContainer c; @@ -118,7 +125,10 @@ main (int argc, char *argv[]) ascii.open ("csma-broadcast.tr"); CsmaHelper::EnableAsciiAll (ascii); - config.ConfigureAttributes (); + if (suppressGtkConfigStore == false) + { + config.ConfigureAttributes (); + } NS_LOG_INFO ("Run Simulation."); Simulator::Run (); diff --git a/regression/tests/test-csma-broadcast.py b/regression/tests/test-csma-broadcast.py index 4c77bc125..8c2939b60 100644 --- a/regression/tests/test-csma-broadcast.py +++ b/regression/tests/test-csma-broadcast.py @@ -2,3 +2,5 @@ """Generic trace-comparison-type regression test.""" +arguments = ["--suppressGtkConfigStore=1"] + From df508b44f6b067aa853940c997664a7fdd6d5a8a Mon Sep 17 00:00:00 2001 From: Sebastien Vincent Date: Mon, 6 Apr 2009 11:12:42 +0200 Subject: [PATCH 09/49] Fix bug 529 (logged values in hex after using Ipv6Address::Print). --- src/node/ipv6-address.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/ipv6-address.cc b/src/node/ipv6-address.cc index adab50e86..ef99226df 100644 --- a/src/node/ipv6-address.cc +++ b/src/node/ipv6-address.cc @@ -338,7 +338,8 @@ void Ipv6Address::Print (std::ostream& os) const << std::hex << std::setw(2) << std::setfill('0') << (unsigned int) m_address[12] << std::hex << std::setw(2) << std::setfill('0') << (unsigned int) m_address[13] << ":" << std::hex << std::setw(2) << std::setfill('0') << (unsigned int) m_address[14] - << std::hex << std::setw(2) << std::setfill('0') << (unsigned int) m_address[15]; + << std::hex << std::setw(2) << std::setfill('0') << (unsigned int) m_address[15] + << std::dec << std::setfill(' '); } bool Ipv6Address::IsLocalhost () const From 199680e76e1497b2323debbe4017c4f9d1d45203 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Mon, 6 Apr 2009 13:05:59 +0100 Subject: [PATCH 10/49] Add code that, when waf is updated in a future version, makes sure python bindings are re-generated whenever the optional features set changes. --- bindings/python/wscript | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bindings/python/wscript b/bindings/python/wscript index 89cdccfec..691a90baa 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -429,8 +429,9 @@ def build(bld): if was_enabled: features.append(name) - bindgen = bld.new_task_gen('command', source=source, target=target, - command=argv, variables=dict(FEATURES=(','.join(features)))) + bindgen = bld.new_task_gen('command', source=source, target=target, command=argv) + bindgen.env['FEATURES'] = ','.join(features) + bindgen.dep_vars = ['FEATURES'] bindgen.before = 'cxx' bindgen.after = 'gen_everything_h_task' From 3a277c0b560ad304051b9d43a31a8828f9a7b128 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Mon, 6 Apr 2009 22:05:02 -0700 Subject: [PATCH 11/49] revert gtk-config-store in csma-broadcast example --- examples/csma-broadcast.cc | 16 ---------------- regression/tests/test-csma-broadcast.py | 2 -- 2 files changed, 18 deletions(-) diff --git a/examples/csma-broadcast.cc b/examples/csma-broadcast.cc index 04be532aa..da5e669a6 100644 --- a/examples/csma-broadcast.cc +++ b/examples/csma-broadcast.cc @@ -36,7 +36,6 @@ #include "ns3/simulator-module.h" #include "ns3/node-module.h" #include "ns3/helper-module.h" -#include "ns3/contrib-module.h" using namespace ns3; @@ -52,21 +51,11 @@ main (int argc, char *argv[]) #endif LogComponentEnable ("CsmaBroadcastExample", LOG_PREFIX_TIME); - // By default, this example will launch the GtkConfigStore - bool suppressGtkConfigStore = false; - // Allow the user to override any of the defaults and the above // Bind()s at run-time, via command-line arguments CommandLine cmd; - cmd.AddValue ("suppressGtkConfigStore", "suppress GtkConfigStore if true", suppressGtkConfigStore); cmd.Parse (argc, argv); - GtkConfigStore config; - if (suppressGtkConfigStore == false) - { - config.ConfigureDefaults (); - } - NS_LOG_INFO ("Create nodes."); NodeContainer c; c.Create (3); @@ -125,11 +114,6 @@ main (int argc, char *argv[]) ascii.open ("csma-broadcast.tr"); CsmaHelper::EnableAsciiAll (ascii); - if (suppressGtkConfigStore == false) - { - config.ConfigureAttributes (); - } - NS_LOG_INFO ("Run Simulation."); Simulator::Run (); Simulator::Destroy (); diff --git a/regression/tests/test-csma-broadcast.py b/regression/tests/test-csma-broadcast.py index 8c2939b60..4c77bc125 100644 --- a/regression/tests/test-csma-broadcast.py +++ b/regression/tests/test-csma-broadcast.py @@ -2,5 +2,3 @@ """Generic trace-comparison-type regression test.""" -arguments = ["--suppressGtkConfigStore=1"] - From 069c9f301e3161d032f2f3e44cf3c699b1748e98 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 9 Apr 2009 15:17:28 +0100 Subject: [PATCH 12/49] Fix receiving bug in BridgeNetDevice regarding packet for the node itself A node containing a BridgeNetDevice has two interfaces, the bridge netdevice adopts one of the MAC addresses as its own address. Whenever a packet has to be sent by the node itself (as opposed to bridged), the packets are sent using that MAC address. The source MAC address used when transmitting in one real netdevice may end up being the MAC address of another real netdevice, and any eventual reply will arrive with a destination MAC address of that other netdevice. Therefore, when receiving packets of type PACKET_OTHERHOST we need to check for this condition, instead of always assuming the packet is to be forwarded. --- src/devices/bridge/bridge-net-device.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/devices/bridge/bridge-net-device.cc b/src/devices/bridge/bridge-net-device.cc index fa314928b..b4a376b8c 100644 --- a/src/devices/bridge/bridge-net-device.cc +++ b/src/devices/bridge/bridge-net-device.cc @@ -111,7 +111,14 @@ BridgeNetDevice::ReceiveFromDevice (Ptr incomingPort, Ptr Date: Mon, 13 Apr 2009 23:10:37 +0100 Subject: [PATCH 13/49] Upgrade to WAF 1.5.4 --- bindings/python/waf | 0 bindings/python/wscript | 29 ++++++-------- regression.py | 26 ++++++++---- src/wscript | 18 ++++----- waf | Bin 81285 -> 84472 bytes wscript | 85 +++++++++++++++++++++++----------------- wutils.py | 12 +++--- 7 files changed, 95 insertions(+), 75 deletions(-) mode change 100644 => 100755 bindings/python/waf diff --git a/bindings/python/waf b/bindings/python/waf old mode 100644 new mode 100755 diff --git a/bindings/python/wscript b/bindings/python/wscript index 691a90baa..d33fdf381 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -273,7 +273,7 @@ class all_ns3_headers_taskgen(TaskGen.task_gen): def apply(self): ## get all of the ns3 headers - ns3_dir_node = Build.bld.path.find_dir("ns3") + ns3_dir_node = self.bld.path.find_dir("ns3") all_headers_inputs = [] for filename in self.to_list(self.source): @@ -284,7 +284,7 @@ class all_ns3_headers_taskgen(TaskGen.task_gen): ## if self.source was empty, include all ns3 headers in enabled modules if not all_headers_inputs: - for ns3headers in Build.bld.all_task_gen: + for ns3headers in self.bld.all_task_gen: if type(ns3headers).__name__ == 'ns3header_taskgen': # XXX: find less hackish way to compare ## skip headers not part of enabled modules if self.env['NS3_ENABLED_MODULES']: @@ -307,7 +307,7 @@ class all_ns3_headers_taskgen(TaskGen.task_gen): pass -def get_modules_and_headers(): +def get_modules_and_headers(bld): """ Gets a dict of module_name => ([module_dep1, module_dep2, ...], [module_header1, module_header2, ...]) @@ -315,13 +315,13 @@ def get_modules_and_headers(): """ retval = {} - for module in Build.bld.all_task_gen: + for module in bld.all_task_gen: if not module.name.startswith('ns3-'): continue module_name = module.name[4:] # strip the ns3- prefix ## find the headers object for this module headers = [] - for ns3headers in Build.bld.all_task_gen: + for ns3headers in bld.all_task_gen: if type(ns3headers).__name__ != 'ns3header_taskgen': # XXX: find less hackish way to compare continue if ns3headers.module != module_name: @@ -338,8 +338,9 @@ class python_scan_task(Task.TaskBase): """ after = 'gen_everything_h_task' before = 'cc cxx' - def __init__(self, curdirnode, env): - super(python_scan_task, self).__init__() + def __init__(self, curdirnode, env, bld): + self.bld = bld + super(python_scan_task, self).__init__(generator=self) self.curdirnode = curdirnode self.env = env @@ -356,7 +357,7 @@ class python_scan_task(Task.TaskBase): os.path.join(self.curdirnode.abspath(), 'ns3modulegen_generated.py'), # output file ] scan = subprocess.Popen(argv, stdin=subprocess.PIPE) - scan.stdin.write(repr(get_modules_and_headers())) + scan.stdin.write(repr(get_modules_and_headers(self.bld))) scan.stdin.close() retval = scan.wait() print "Scan finished with exit code", retval @@ -365,7 +366,7 @@ class python_scan_task(Task.TaskBase): # signal stop (we generated files into the source dir and WAF # can't cope with it, so we have to force the user to restart # WAF) - Build.bld.generator.stop = 1 + self.bld.generator.stop = 1 return 0 @@ -384,7 +385,7 @@ def build(bld): if Options.options.python_scan: if not env['ENABLE_PYTHON_SCANNING']: raise Utils.WafError("Cannot re-scan python bindings: (py)gccxml not available") - python_scan_task(bld.path, env) + python_scan_task(bld.path, env, bld) return ## Get a list of scanned modules; the set of scanned modules @@ -412,7 +413,7 @@ def build(bld): 'ns3modulegen.log', ] argv = ['NS3_ENABLED_FEATURES=${FEATURES}', '${PYTHON}', '${SRC[0]}', '${TGT[0]}'] - argv.extend(get_modules_and_headers().iterkeys()) + argv.extend(get_modules_and_headers(bld).iterkeys()) for module in scanned_modules: source.append("ns3_module_%s.py" % module) local = "ns3_module_%s__local.py" % module @@ -434,12 +435,8 @@ def build(bld): bindgen.dep_vars = ['FEATURES'] bindgen.before = 'cxx' bindgen.after = 'gen_everything_h_task' + bindgen.name = "pybindgen-command" - ## we build python bindings if either we have the tools to - ## generate them or if the pregenerated source file is already - ## present in the source dir. - if env['ENABLE_PYTHON_BINDINGS'] \ - or os.path.exists(os.path.join(bld.path.abspath(), 'ns3module.cc')): pymod = bld.new_task_gen('cxx', 'shlib', 'pyext') pymod.source = ['ns3module.cc', 'ns3module_helpers.cc'] pymod.includes = '.' diff --git a/regression.py b/regression.py index f64f261ba..d6ae741a6 100644 --- a/regression.py +++ b/regression.py @@ -6,7 +6,6 @@ import pproc as subprocess import errno # WAF modules -import Build import Options import Utils import Task @@ -66,9 +65,11 @@ class regression_test_task(Task.TaskBase): after = 'cc cxx cc_link cxx_link' color = 'BLUE' - def __init__(self, env, test_name, test_scripts_dir, build_traces_dir, reference_traces): - super(regression_test_task, self).__init__() + def __init__(self, bld, env, test_name, test_scripts_dir, build_traces_dir, reference_traces): + self.bld = bld + self.generator = self self.env = env + super(regression_test_task, self).__init__(generator=self, env=env) self.test_name = test_name self.test_scripts_dir = test_scripts_dir self.build_traces_dir = build_traces_dir @@ -77,6 +78,9 @@ class regression_test_task(Task.TaskBase): def __str__(self): return 'regression-test (%s)\n' % self.test_name + def runnable_status(self): + return Task.RUN_ME + def run(self): """Run a single test""" sys.path.insert(0, self.test_scripts_dir) @@ -119,7 +123,7 @@ class regression_test_task(Task.TaskBase): if Options.options.regression_generate: # clean the target dir try: - shutil.rmtree(trace_output_path) + shutil.rmtree(reference_traces_path) except OSError, ex: if ex.errno not in [errno.ENOENT]: raise @@ -206,13 +210,17 @@ class regression_test_collector_task(Task.TaskBase): after = 'regression_test_task' color = 'BLUE' - def __init__(self, test_tasks): - super(regression_test_collector_task, self).__init__() + def __init__(self, bld, test_tasks): + self.bld = bld + super(regression_test_collector_task, self).__init__(generator=self) self.test_tasks = test_tasks def __str__(self): return 'regression-test-collector\n' + def runnable_status(self): + return Task.RUN_ME + def run(self): failed_tests = [test for test in self.test_tasks if test.result is not None and test.result != 0] skipped_tests = [test for test in self.test_tasks if test.result is None] @@ -256,5 +264,7 @@ def run_regression(bld, reference_traces): build_traces_dir = bld.path.find_or_declare('regression/traces').abspath(bld.env) tasks = [] for test in tests: - tasks.append(regression_test_task(bld.env, test, test_scripts_dir, build_traces_dir, reference_traces)) - regression_test_collector_task(tasks) + task = regression_test_task(bld, bld.env, test, test_scripts_dir, build_traces_dir, reference_traces) + #bld.task_manager.add_task(task) + tasks.append(task) + regression_test_collector_task(bld, tasks) diff --git a/src/wscript b/src/wscript index 7977217a1..b9192858a 100644 --- a/src/wscript +++ b/src/wscript @@ -9,8 +9,7 @@ import TaskGen import Task import Options import Build -from Utils import md5 - +import Utils all_modules = ( 'core', @@ -109,7 +108,7 @@ class ns3header_taskgen(TaskGen.task_gen): def apply(self): if self.module is None: raise Utils.WafError("'module' missing on ns3headers object %s" % self) - ns3_dir_node = Build.bld.path.find_dir("ns3") + ns3_dir_node = self.bld.path.find_dir("ns3") if self.sub_dir is not None: ns3_dir_node = ns3_dir_node.find_dir(self.sub_dir) for filename in self.to_list(self.source): @@ -176,7 +175,7 @@ class gen_ns3_module_header_task(Task.Task): return 0 def sig_explicit_deps(self): - m = md5() + m = Utils.md5() m.update('\n'.join([node.abspath(self.env) for node in self.inputs])) return m.digest() @@ -185,7 +184,7 @@ class gen_ns3_module_header_task(Task.Task): return self.uid except AttributeError: "this is not a real hot zone, but we want to avoid surprizes here" - m = md5() + m = Utils.md5() m.update("ns-3-module-header-%s" % self.module) self.uid = m.digest() return self.uid @@ -199,13 +198,12 @@ class ns3moduleheader_taskgen(TaskGen.task_gen): COLOR = 'BLUE' def __init__(self, *args, **kwargs): super(ns3moduleheader_taskgen, self).__init__(*args, **kwargs) - self.module_name = None def apply(self): ## get all of the ns3 headers - ns3_dir_node = Build.bld.path.find_dir("ns3") + ns3_dir_node = self.bld.path.find_dir("ns3") all_headers_inputs = [] - for ns3headers in Build.bld.all_task_gen: + for ns3headers in self.bld.all_task_gen: if isinstance(ns3headers, ns3header_taskgen): if ns3headers.module != self.module: continue @@ -215,8 +213,10 @@ class ns3moduleheader_taskgen(TaskGen.task_gen): if node is None: fatal("missing header file %s" % (source,)) all_headers_inputs.append(node) + if not all_headers_inputs: + raise Utils.WscriptError("error finding headers for module %s" % self.module) assert all_headers_inputs - module_obj = Build.bld.name_to_obj("ns3-" + self.module, self.env) + module_obj = self.bld.name_to_obj("ns3-" + self.module, self.env) assert module_obj is not None all_headers_outputs = [ns3_dir_node.find_or_declare("%s-module.h" % self.module)] task = self.create_task('gen_ns3_module_header', self.env) diff --git a/waf b/waf index 23b5ac178aef6cd9db85e96d154f28e479c1e645..25ac3fc18a19c49be7d2eafa7b0f4f8fb8bd57f2 100755 GIT binary patch delta 81909 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 S$rRy2L9^x_APOTqJv|Bp{lTID diff --git a/wscript b/wscript index b1a288ddd..85a44430c 100644 --- a/wscript +++ b/wscript @@ -10,6 +10,7 @@ import os.path # WAF modules import pproc as subprocess import Options + import Logs import TaskGen import Constants @@ -23,6 +24,7 @@ Task.algotype = Constants.JOBCONTROL # so that Task.maxjobs=1 takes effect import Utils import Build import Configure +import Scripting import cflags # override the build profiles from waf cflags.profiles = { @@ -133,7 +135,7 @@ def set_options(opt): action="store_true", default=False, dest='valgrind') opt.add_option('--shell', - help=('Run a shell with an environment suitably modified to run locally built programs'), + help=('DEPRECATED (run ./waf shell)'), action="store_true", default=False, dest='shell') opt.add_option('--enable-sudo', @@ -161,7 +163,7 @@ def set_options(opt): opt.sub_options('src/internet-stack') -def check_compilation_flag(conf, flag): +def _check_compilation_flag(conf, flag): """ Checks if the C++ compiler accepts a certain compilation flag or flags flag: can be a string or a list of strings @@ -186,7 +188,7 @@ def report_optional_feature(conf, name, caption, was_enabled, reason_not_enabled def configure(conf): # attach some extra methods - conf.check_compilation_flag = types.MethodType(check_compilation_flag, conf) + conf.check_compilation_flag = types.MethodType(_check_compilation_flag, conf) conf.report_optional_feature = types.MethodType(report_optional_feature, conf) conf.env['NS3_OPTIONAL_FEATURES'] = [] @@ -235,7 +237,7 @@ def configure(conf): env.append_value('CXXDEFINES', 'RUN_SELF_TESTS') if env['COMPILER_CXX'] == 'g++' and 'CXXFLAGS' not in os.environ: - if check_compilation_flag(conf, '-Wno-error=deprecated-declarations'): + if conf.check_compilation_flag('-Wno-error=deprecated-declarations'): env.append_value('CXXFLAGS', '-Wno-error=deprecated-declarations') if Options.options.build_profile == 'debug': @@ -363,6 +365,7 @@ def add_scratch_programs(bld): def build(bld): + wutils.bld = bld if Options.options.no_task_lines: import Runner def null_printout(s): @@ -378,21 +381,6 @@ def build(bld): variant_env = bld.env_of_name(variant_name) bld.all_envs['default'] = variant_env - if Options.options.shell: - run_shell() - raise SystemExit(0) - - if Options.options.doxygen: - doxygen() - raise SystemExit(0) - - check_shell() - - if Options.options.doxygen: - doxygen() - raise SystemExit(0) - - print "Entering directory `%s'" % os.path.join(bld.path.abspath(), 'build') # process subfolders from here bld.add_subdirs('src') bld.add_subdirs('samples utils examples') @@ -427,14 +415,14 @@ def build(bld): changed = True ## remove objects that depend on modules not listed - for obj in list(Build.bld.all_task_gen): + for obj in list(bld.all_task_gen): if hasattr(obj, 'ns3_module_dependencies'): for dep in obj.ns3_module_dependencies: if dep not in modules: - Build.bld.all_task_gen.remove(obj) + bld.all_task_gen.remove(obj) break if obj.name in env['NS3_MODULES'] and obj.name not in modules: - Build.bld.all_task_gen.remove(obj) + bld.all_task_gen.remove(obj) ## Create a single ns3 library containing all enabled modules lib = bld.new_task_gen('cxx', 'shlib') @@ -468,11 +456,13 @@ def build(bld): regression.run_regression(bld, regression_traces) -def shutdown(): - env = Build.bld.env - if Options.commands['check']: - _run_waf_check() +def shutdown(ctx): + bld = wutils.bld + env = bld.env + + #if Options.commands['check']: + # _run_waf_check() if Options.options.lcov_report: lcov_report() @@ -485,9 +475,26 @@ def shutdown(): wutils.run_python_program(Options.options.pyrun) raise SystemExit(0) -def _run_waf_check(): + if Options.options.shell: + raise Utils.WafError("Run `./waf shell' now, instead of `./waf shell'") + + if Options.options.doxygen: + doxygen() + raise SystemExit(0) + + check_shell(bld) + + if Options.options.doxygen: + doxygen() + raise SystemExit(0) + + +check_context = Build.BuildContext +def check(bld): + "run the NS-3 unit tests" + Scripting.build(bld) ## generate the trace sources list docs - env = Build.bld.env + env = bld.env proc_env = wutils.get_proc_env() try: program_obj = wutils.find_program('print-introspected-doxygen', env) @@ -514,14 +521,14 @@ def _run_waf_check(): -def check_shell(): +def check_shell(bld): if 'NS3_MODULE_PATH' not in os.environ: return - env = Build.bld.env + env = bld.env correct_modpath = os.pathsep.join(env['NS3_MODULE_PATH']) found_modpath = os.environ['NS3_MODULE_PATH'] if found_modpath != correct_modpath: - msg = ("Detected shell (waf --shell) with incorrect configuration\n" + msg = ("Detected shell (./waf shell) with incorrect configuration\n" "=========================================================\n" "Possible reasons for this problem:\n" " 1. You switched to another ns-3 tree from inside this shell\n" @@ -533,13 +540,19 @@ def check_shell(): raise Utils.WafError(msg) -def run_shell(): +shell_context = Build.BuildContext +def shell(ctx): + """run a shell with an environment suitably modified to run locally built programs""" + + #make sure we build first" + Scripting.build(ctx) + if sys.platform == 'win32': shell = os.environ.get("COMSPEC", "cmd.exe") else: shell = os.environ.get("SHELL", "/bin/sh") - env = Build.bld.env + env = wutils.bld.env wutils.run_argv([shell], {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])}) def doxygen(): @@ -598,7 +611,7 @@ from Scripting import dist_exts, excludes, BLDDIR import Utils import os -def copytree(src, dst, symlinks=False, excludes=(), build_dir=None): +def _copytree(src, dst, symlinks=False, excludes=(), build_dir=None): """Recursively copy a directory tree using copy2(). The destination directory must not already exist. @@ -668,7 +681,7 @@ def copytree(src, dst, symlinks=False, excludes=(), build_dir=None): def DistDir(appname, version): - "make a distribution directory with all the sources in it" + #"make a distribution directory with all the sources in it" import shutil # Our temporary folder where to put our files @@ -683,7 +696,7 @@ def DistDir(appname, version): build_dir = getattr(Utils.g_module, BLDDIR, None) # Copy everything into the new folder - copytree('.', TMPFOLDER, excludes=excludes, build_dir=build_dir) + _copytree('.', TMPFOLDER, excludes=excludes, build_dir=build_dir) # TODO undocumented hook dist_hook = getattr(Utils.g_module, 'dist_hook', None) diff --git a/wutils.py b/wutils.py index 33cc5a4f7..38a6307b1 100644 --- a/wutils.py +++ b/wutils.py @@ -16,6 +16,7 @@ import Build # these are set from the main wscript file APPNAME=None VERSION=None +bld=None # # The last part of the path name to use to find the regression traces tarball. @@ -65,7 +66,7 @@ def find_program(program_name, env): launch_dir = os.path.abspath(Options.cwd_launch) top_dir = os.path.abspath(Options.launch_dir) found_programs = [] - for obj in Build.bld.all_task_gen: + for obj in bld.all_task_gen: if not getattr(obj, 'is_ns3_program', False): continue @@ -84,7 +85,7 @@ def find_program(program_name, env): % (program_name, found_programs)) def get_proc_env(os_env=None): - env = Build.bld.env + env = bld.env if sys.platform == 'linux2': pathvar = 'LD_LIBRARY_PATH' elif sys.platform == 'darwin': @@ -111,7 +112,7 @@ def get_proc_env(os_env=None): else: proc_env[pathvar] = os.pathsep.join(list(env['NS3_MODULE_PATH'])) - pymoddir = Build.bld.path.find_dir('bindings/python').abspath(env) + pymoddir = bld.path.find_dir('bindings/python').abspath(env) if 'PYTHONPATH' in proc_env: proc_env['PYTHONPATH'] = os.pathsep.join([pymoddir] + [proc_env['PYTHONPATH']]) else: @@ -121,7 +122,6 @@ def get_proc_env(os_env=None): def run_argv(argv, os_env=None, cwd=None): proc_env = get_proc_env(os_env) - #env = Build.bld.env retval = subprocess.Popen(argv, env=proc_env, cwd=cwd).wait() if retval: raise Utils.WafError("Command %s exited with code %i" % (argv, retval)) @@ -133,7 +133,7 @@ def get_run_program(program_string, command_template=None): run_program(program_string, command_template). """ #print "get_run_program_argv(program_string=%r, command_template=%r)" % (program_string, command_template) - env = Build.bld.env + env = bld.env if command_template in (None, '%s'): argv = shlex.split(program_string) @@ -187,7 +187,7 @@ def run_program(program_string, command_template=None, cwd=None): def run_python_program(program_string): - env = Build.bld.env + env = bld.env execvec = shlex.split(program_string) if (Options.options.cwd_launch): cwd = Options.options.cwd_launch From a9fce22aa77086e4dae7ca35b76d4ec757656758 Mon Sep 17 00:00:00 2001 From: Nicola Baldo Date: Wed, 15 Apr 2009 13:56:01 +0200 Subject: [PATCH 14/49] noiseFloor -> noiseFigure (bug 543) --- src/devices/wifi/interference-helper.cc | 13 +++++++------ src/devices/wifi/interference-helper.h | 6 +++--- src/devices/wifi/yans-wifi-phy.cc | 24 +++++++++++++++--------- src/devices/wifi/yans-wifi-phy.h | 4 ++-- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/devices/wifi/interference-helper.cc b/src/devices/wifi/interference-helper.cc index fa50b5674..186e6c44d 100644 --- a/src/devices/wifi/interference-helper.cc +++ b/src/devices/wifi/interference-helper.cc @@ -137,15 +137,15 @@ InterferenceHelper::GetMaxPacketDuration (void) const } void -InterferenceHelper::SetNoiseFloorW (double noiseFloor) +InterferenceHelper::SetNoiseFigure (double value) { - m_noiseFloorW = noiseFloor; + m_noiseFigure = value; } double -InterferenceHelper::GetNoiseFloorW (void) const +InterferenceHelper::GetNoiseFigure (void) const { - return m_noiseFloorW; + return m_noiseFigure; } void @@ -260,9 +260,10 @@ InterferenceHelper::CalculateSnr (double signal, double noiseInterference, WifiM { // thermal noise at 290K in J/s = W static const double BOLTZMANN = 1.3803e-23; + // Nt is the power of thermal noise in W double Nt = BOLTZMANN * 290.0 * mode.GetBandwidth (); - // receiver noise Floor (W) - double noiseFloor = m_noiseFloorW * Nt; + // receiver noise Floor (W) which accounts for thermal noise and non-idealities of the receiver + double noiseFloor = m_noiseFigure * Nt; double noise = noiseFloor + noiseInterference; double snr = signal / noise; return snr; diff --git a/src/devices/wifi/interference-helper.h b/src/devices/wifi/interference-helper.h index 29d284501..8e6005031 100644 --- a/src/devices/wifi/interference-helper.h +++ b/src/devices/wifi/interference-helper.h @@ -50,10 +50,10 @@ public: ~InterferenceHelper (); void Configure80211aParameters (void); - void SetNoiseFloorW (double noiseFloor); + void SetNoiseFigure (double value); void SetErrorRateModel (Ptr rate); - double GetNoiseFloorW (void) const; + double GetNoiseFigure (void) const; Ptr GetErrorRateModel (void) const; @@ -99,7 +99,7 @@ private: WifiMode m_shortPlcpHeaderMode; uint32_t m_plcpHeaderLength; Time m_maxPacketDuration; - double m_noiseFloorW; + double m_noiseFigure; /**< noise figure (linear) */ Events m_events; bool m_80211a; Ptr m_errorRateModel; diff --git a/src/devices/wifi/yans-wifi-phy.cc b/src/devices/wifi/yans-wifi-phy.cc index 369bb589f..f57ebdfaf 100644 --- a/src/devices/wifi/yans-wifi-phy.cc +++ b/src/devices/wifi/yans-wifi-phy.cc @@ -93,11 +93,17 @@ YansWifiPhy::GetTypeId (void) MakeDoubleAccessor (&YansWifiPhy::SetTxPowerStart, &YansWifiPhy::GetTxPowerStart), MakeDoubleChecker ()) - .AddAttribute ("RxNoise", - "Ratio of energy lost by receiver (dB).", + .AddAttribute ("RxNoiseFigure", + "Loss (dB) in the Signal-to-Noise-Ratio due to non-idealities in the receiver." + " According to Wikipedia (http://en.wikipedia.org/wiki/Noise_figure), this is " + "\"the difference in decibels (dB) between" + " the noise output of the actual receiver to the noise output of an " + " ideal receiver with the same overall gain and bandwidth when the receivers " + " are connected to sources at the standard noise temperature T0 (usually 290 K)\"." + " For", DoubleValue (7), - MakeDoubleAccessor (&YansWifiPhy::SetRxNoise, - &YansWifiPhy::GetRxNoise), + MakeDoubleAccessor (&YansWifiPhy::SetRxNoiseFigure, + &YansWifiPhy::GetRxNoiseFigure), MakeDoubleChecker ()) .AddAttribute ("Standard", "The standard chosen configures a set of transmission modes" " and some PHY-specific constants.", @@ -155,10 +161,10 @@ YansWifiPhy::SetStandard (enum WifiPhyStandard standard) void -YansWifiPhy::SetRxNoise (double db) +YansWifiPhy::SetRxNoiseFigure (double noiseFigureDb) { - NS_LOG_FUNCTION (this << db); - m_interference.SetNoiseFloorW (DbToRatio (db)); + NS_LOG_FUNCTION (this << noiseFigureDb); + m_interference.SetNoiseFigure (DbToRatio (noiseFigureDb)); } void YansWifiPhy::SetTxPowerStart (double start) @@ -219,9 +225,9 @@ YansWifiPhy::SetMobility (Ptr mobility) } double -YansWifiPhy::GetRxNoise (void) const +YansWifiPhy::GetRxNoiseFigure (void) const { - return RatioToDb (m_interference.GetNoiseFloorW ()); + return RatioToDb (m_interference.GetNoiseFigure ()); } double YansWifiPhy::GetTxPowerStart (void) const diff --git a/src/devices/wifi/yans-wifi-phy.h b/src/devices/wifi/yans-wifi-phy.h index faf6e85c4..49d5c2d85 100644 --- a/src/devices/wifi/yans-wifi-phy.h +++ b/src/devices/wifi/yans-wifi-phy.h @@ -75,7 +75,7 @@ public: WifiPreamble preamble); void SetStandard (enum WifiPhyStandard standard); - void SetRxNoise (double ratio); + void SetRxNoiseFigure (double noiseFigureDb); void SetTxPowerStart (double start); void SetTxPowerEnd (double end); void SetNTxPower (uint32_t n); @@ -86,7 +86,7 @@ public: void SetErrorRateModel (Ptr rate); void SetDevice (Ptr device); void SetMobility (Ptr mobility); - double GetRxNoise (void) const; + double GetRxNoiseFigure (void) const; double GetTxGain (void) const; double GetRxGain (void) const; double GetEdThreshold (void) const; From 3f90d9595ab5510b6f767c461b158dd537b0d2e8 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 09:25:32 +0200 Subject: [PATCH 15/49] scan python --- bindings/python/ns3_module_core.py | 8 ++++---- bindings/python/ns3_module_wifi.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bindings/python/ns3_module_core.py b/bindings/python/ns3_module_core.py index 71322205c..82c9a71c1 100644 --- a/bindings/python/ns3_module_core.py +++ b/bindings/python/ns3_module_core.py @@ -1984,7 +1984,7 @@ def register_Ns3TracedValue__Unsigned_int_methods(root_module, cls): cls.add_method('ConnectWithoutContext', 'void', [param('ns3::CallbackBase const &', 'cb')]) - ## traced-value.h: void ns3::TracedValue::Connect(ns3::CallbackBase const & cb, std::basic_string,std::allocator > path) [member function] + ## traced-value.h: void ns3::TracedValue::Connect(ns3::CallbackBase const & cb, std::string path) [member function] cls.add_method('Connect', 'void', [param('ns3::CallbackBase const &', 'cb'), param('std::string', 'path')]) @@ -1992,7 +1992,7 @@ def register_Ns3TracedValue__Unsigned_int_methods(root_module, cls): cls.add_method('DisconnectWithoutContext', 'void', [param('ns3::CallbackBase const &', 'cb')]) - ## traced-value.h: void ns3::TracedValue::Disconnect(ns3::CallbackBase const & cb, std::basic_string,std::allocator > path) [member function] + ## traced-value.h: void ns3::TracedValue::Disconnect(ns3::CallbackBase const & cb, std::string path) [member function] cls.add_method('Disconnect', 'void', [param('ns3::CallbackBase const &', 'cb'), param('std::string', 'path')]) @@ -2140,7 +2140,7 @@ def register_functions(root_module): module.add_function('TypeNameGet', 'std::string', [], - template_parameters=['long']) + template_parameters=['long long']) ## type-name.h: extern std::string ns3::TypeNameGet() [free function] module.add_function('TypeNameGet', 'std::string', @@ -2160,7 +2160,7 @@ def register_functions(root_module): module.add_function('TypeNameGet', 'std::string', [], - template_parameters=['unsigned long']) + template_parameters=['unsigned long long']) ## type-name.h: extern std::string ns3::TypeNameGet() [free function] module.add_function('TypeNameGet', 'std::string', diff --git a/bindings/python/ns3_module_wifi.py b/bindings/python/ns3_module_wifi.py index 564128159..85394a0a4 100644 --- a/bindings/python/ns3_module_wifi.py +++ b/bindings/python/ns3_module_wifi.py @@ -228,8 +228,8 @@ def register_Ns3InterferenceHelper_methods(root_module, cls): 'ns3::Ptr< ns3::ErrorRateModel >', [], is_const=True) - ## interference-helper.h: double ns3::InterferenceHelper::GetNoiseFloorW() const [member function] - cls.add_method('GetNoiseFloorW', + ## interference-helper.h: double ns3::InterferenceHelper::GetNoiseFigure() const [member function] + cls.add_method('GetNoiseFigure', 'double', [], is_const=True) @@ -237,10 +237,10 @@ def register_Ns3InterferenceHelper_methods(root_module, cls): cls.add_method('SetErrorRateModel', 'void', [param('ns3::Ptr< ns3::ErrorRateModel >', 'rate')]) - ## interference-helper.h: void ns3::InterferenceHelper::SetNoiseFloorW(double noiseFloor) [member function] - cls.add_method('SetNoiseFloorW', + ## interference-helper.h: void ns3::InterferenceHelper::SetNoiseFigure(double value) [member function] + cls.add_method('SetNoiseFigure', 'void', - [param('double', 'noiseFloor')]) + [param('double', 'value')]) return def register_Ns3InterferenceHelperSnrPer_methods(root_module, cls): @@ -1707,10 +1707,10 @@ def register_Ns3YansWifiPhy_methods(root_module, cls): cls.add_method('SetStandard', 'void', [param('ns3::WifiPhyStandard', 'standard')]) - ## yans-wifi-phy.h: void ns3::YansWifiPhy::SetRxNoise(double ratio) [member function] - cls.add_method('SetRxNoise', + ## yans-wifi-phy.h: void ns3::YansWifiPhy::SetRxNoiseFigure(double noiseFigureDb) [member function] + cls.add_method('SetRxNoiseFigure', 'void', - [param('double', 'ratio')]) + [param('double', 'noiseFigureDb')]) ## yans-wifi-phy.h: void ns3::YansWifiPhy::SetTxPowerStart(double start) [member function] cls.add_method('SetTxPowerStart', 'void', @@ -1751,8 +1751,8 @@ def register_Ns3YansWifiPhy_methods(root_module, cls): cls.add_method('SetMobility', 'void', [param('ns3::Ptr< ns3::Object >', 'mobility')]) - ## yans-wifi-phy.h: double ns3::YansWifiPhy::GetRxNoise() const [member function] - cls.add_method('GetRxNoise', + ## yans-wifi-phy.h: double ns3::YansWifiPhy::GetRxNoiseFigure() const [member function] + cls.add_method('GetRxNoiseFigure', 'double', [], is_const=True) From 9613fb839c38fd8621f8d1e5068de154bab65c68 Mon Sep 17 00:00:00 2001 From: Timo Bingmann Date: Thu, 16 Apr 2009 10:07:48 +0200 Subject: [PATCH 16/49] bug 475: GammaVariable and ErlangVariable --- samples/main-random-variable.cc | 377 ++++++++++++++++++++++++++++++++ samples/wscript | 3 + src/core/random-variable.cc | 224 +++++++++++++++++++ src/core/random-variable.h | 107 ++++++++- 4 files changed, 708 insertions(+), 3 deletions(-) create mode 100644 samples/main-random-variable.cc diff --git a/samples/main-random-variable.cc b/samples/main-random-variable.cc new file mode 100644 index 000000000..de16cd6da --- /dev/null +++ b/samples/main-random-variable.cc @@ -0,0 +1,377 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2008 Timo Bingmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Timo Bingmann + */ +#include "ns3/random-variable.h" +#include "ns3/gnuplot.h" +#include +#include + +using namespace ns3; + +/// Round a double number to the given precision. e.g. dround(0.234, 0.1) = 0.2 +/// and dround(0.257, 0.1) = 0.3 +double dround(double number, double precision) +{ + number /= precision; + if (number >= 0) + number = std::floor(number + 0.5); + else + number = std::ceil(number - 0.5); + number *= precision; + return number; +} + +static GnuplotDataset +Histogramm (RandomVariable rndvar, unsigned int probes, double precision, const std::string& title, bool notcontinous = false) +{ + typedef std::map histogramm_maptype; + histogramm_maptype histogramm; + + for(unsigned int i = 0; i < probes; ++i) + { + double val = dround( rndvar.GetValue(), precision ); + + ++histogramm[val]; + } + + Gnuplot2dDataset data; + data.SetTitle(title); + + if (notcontinous) + { + data.SetStyle(Gnuplot2dDataset::IMPULSES); + } + + for(histogramm_maptype::const_iterator hi = histogramm.begin(); + hi != histogramm.end(); ++hi) + { + data.Add(hi->first, (double)hi->second / (double)probes / precision); + } + + return data; +} + +int main (int argc, char *argv[]) +{ + unsigned int probes = 1000000; + double precision = 0.01; + + GnuplotCollection gnuplots("main-random-variables.pdf"); + gnuplots.SetTerminal("pdf enhanced"); + + { + Gnuplot plot; + plot.SetTitle("UniformVariable"); + plot.AppendExtra("set yrange [0:]"); + + plot.AddDataset( Histogramm(UniformVariable(0.0, 1.0), probes, precision, + "UniformVariable [0.0 .. 1.0)") ); + + plot.AddDataset( Gnuplot2dFunction("0.1", + "0 <= x && x <= 1 ? 1.0 : 0") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("ExponentialVariable"); + plot.AppendExtra("set xrange [0:8]"); + plot.AppendExtra("ExpDist(x,l) = 1/l * exp(-1/l * x)"); + + plot.AddDataset( Histogramm(ExponentialVariable(0.5), probes, precision, + "ExponentialVariable m=0.5") ); + + plot.AddDataset( Gnuplot2dFunction("ExponentialDistribution mean 0.5", + "ExpDist(x, 0.5)") ); + + plot.AddDataset( Histogramm(ExponentialVariable(1.0), probes, precision, + "ExponentialVariable m=1") ); + + plot.AddDataset( Gnuplot2dFunction("ExponentialDistribution mean 1.0", + "ExpDist(x, 1.0)") ); + + plot.AddDataset( Histogramm(ExponentialVariable(1.5), probes, precision, + "ExponentialVariable m=1.5") ); + + plot.AddDataset( Gnuplot2dFunction("ExponentialDistribution mean 1.5", + "ExpDist(x, 1.5)") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("ParetoVariable"); + plot.AppendExtra("set xrange [0:2]"); + + plot.AddDataset( Histogramm(ParetoVariable(1.0, 1.5), probes, precision, + "ParetoVariable m=1.0 s=1.5") ); + + plot.AddDataset( Histogramm(ParetoVariable(1.0, 2.0), probes, precision, + "ParetoVariable m=1.0 s=2.0") ); + + plot.AddDataset( Histogramm(ParetoVariable(1.0, 2.5), probes, precision, + "ParetoVariable m=1.0 s=2.5") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("WeibullVariable"); + plot.AppendExtra("set xrange [0:3]"); + + plot.AddDataset( Histogramm(WeibullVariable(1.0, 1.0), probes, precision, + "WeibullVariable m=1.0 s=1.0") ); + + plot.AddDataset( Histogramm(WeibullVariable(1.0, 2.0), probes, precision, + "WeibullVariable m=1.0 s=2.0") ); + + plot.AddDataset( Histogramm(WeibullVariable(1.0, 3.0), probes, precision, + "WeibullVariable m=1.0 s=3.0") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("NormalVariable"); + plot.AppendExtra("set xrange [-3:3]"); + plot.AppendExtra("NormalDist(x,m,s) = 1 / (s * sqrt(2*pi)) * exp(-1.0 / 2.0 * ((x-m) / s)**2)"); + + plot.AddDataset( Histogramm(NormalVariable(0.0, 1.0), probes, precision, + "NormalVariable m=0.0 v=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=1.0", + "NormalDist(x,0.0,1.0)") ); + + plot.AddDataset( Histogramm(NormalVariable(0.0, 2.0), probes, precision, + "NormalVariable m=0.0 v=2.0") ); + + plot.AddDataset( Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=sqrt(2.0)", + "NormalDist(x,0.0,sqrt(2.0))") ); + + plot.AddDataset( Histogramm(NormalVariable(0.0, 3.0), probes, precision, + "NormalVariable m=0.0 v=3.0") ); + + plot.AddDataset( Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=sqrt(3.0)", + "NormalDist(x,0.0,sqrt(3.0))") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("EmpiricalVariable"); + plot.AppendExtra("set xrange [*:*]"); + + EmpiricalVariable emp1; + emp1.CDF(0.0, 0.0 / 15.0); + emp1.CDF(0.2, 1.0 / 15.0); + emp1.CDF(0.4, 3.0 / 15.0); + emp1.CDF(0.6, 6.0 / 15.0); + emp1.CDF(0.8, 10.0 / 15.0); + emp1.CDF(1.0, 15.0 / 15.0); + + plot.AddDataset( Histogramm(emp1, probes, precision, + "EmpiricalVariable (Stairs)") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("DeterministicVariable"); + plot.AppendExtra("set xrange [*:*]"); + + double values[] = { 0.0, 0.2, 0.2, 0.4, 0.2, 0.6, 0.8, 0.8, 1.0 }; + DeterministicVariable det1 (values, sizeof(values) / sizeof(values[0])); + + plot.AddDataset( Histogramm(det1, probes, precision, + "DeterministicVariable", true) ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("LogNormalVariable"); + plot.AppendExtra("set xrange [0:3]"); + + plot.AppendExtra("LogNormalDist(x,m,s) = 1.0/x * NormalDist(log(x), m, s)"); + + plot.AddDataset( Histogramm(LogNormalVariable(0.0, 1.0), probes, precision, + "LogNormalVariable m=0.0 s=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("LogNormalDist(x, 0.0, 1.0)", + "LogNormalDist(x, 0.0, 1.0)") ); + + plot.AddDataset( Histogramm(LogNormalVariable(0.0, 0.5), probes, precision, + "LogNormalVariable m=0.0 s=0.5") ); + + plot.AddDataset( Histogramm(LogNormalVariable(0.0, 0.25), probes, precision, + "LogNormalVariable m=0.0 s=0.25") ); + + plot.AddDataset( Gnuplot2dFunction("LogNormalDist(x, 0.0, 0.25)", + "LogNormalDist(x, 0.0, 0.25)") ); + + plot.AddDataset( Histogramm(LogNormalVariable(0.0, 0.125), probes, precision, + "LogNormalVariable m=0.0 s=0.125") ); + + plot.AddDataset( Histogramm(LogNormalVariable(0.0, 2.0), probes, precision, + "LogNormalVariable m=0.0 s=2.0") ); + + plot.AddDataset( Gnuplot2dFunction("LogNormalDist(x, 0.0, 2.0)", + "LogNormalDist(x, 0.0, 2.0)") ); + + plot.AddDataset( Histogramm(LogNormalVariable(0.0, 2.5), probes, precision, + "LogNormalVariable m=0.0 s=2.5") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("TriangularVariable"); + plot.AppendExtra("set xrange [*:*]"); + + plot.AddDataset( Histogramm(TriangularVariable(0.0, 1.0, 0.5), probes, precision, + "TriangularVariable [0.0 .. 1.0) m=0.5") ); + + plot.AddDataset( Histogramm(TriangularVariable(0.0, 1.0, 0.4), probes, precision, + "TriangularVariable [0.0 .. 1.0) m=0.4") ); + + plot.AddDataset( Histogramm(TriangularVariable(0.0, 1.0, 0.65), probes, precision, + "TriangularVariable [0.0 .. 1.0) m=0.65") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("GammaVariable"); + plot.AppendExtra("set xrange [0:10]"); + plot.AppendExtra("set yrange [0:1]"); + plot.AppendExtra("GammaDist(x,a,b) = x**(a-1) * 1/b**a * exp(-x/b) / gamma(a)"); + + plot.AppendExtra("set label 1 '{/Symbol g}(x,{/Symbol a},{/Symbol b}) = x^{/Symbol a-1} e^{-x {/Symbol b}^{-1}} ( {/Symbol b}^{/Symbol a} {/Symbol G}({/Symbol a}) )^{-1}' at 0.7, 0.9"); + + plot.AddDataset( Histogramm(GammaVariable(1.0, 1.0), probes, precision, + "GammaVariable a=1.0 b=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 1.0, 1.0)", + "GammaDist(x, 1.0, 1.0)") ); + + plot.AddDataset( Histogramm(GammaVariable(1.5, 1.0), probes, precision, + "GammaVariable a=1.5 b=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 1.5, 1.0)", + "GammaDist(x, 1.5, 1.0)") ); + + plot.AddDataset( Histogramm(GammaVariable(2.0, 1.0), probes, precision, + "GammaVariable a=2.0 b=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 2.0, 1.0)", + "GammaDist(x, 2.0, 1.0)") ); + + plot.AddDataset( Histogramm(GammaVariable(4.0, 1.0), probes, precision, + "GammaVariable a=4.0 b=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 4.0, 1.0)", + "GammaDist(x, 4.0, 1.0)") ); + + plot.AddDataset( Histogramm(GammaVariable(2.0, 2.0), probes, precision, + "GammaVariable a=2.0 b=2.0") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 2.0, 2.0)", + "GammaDist(x, 2.0, 2.0)") ); + + plot.AddDataset( Histogramm(GammaVariable(2.5, 3.0), probes, precision, + "GammaVariable a=2.5 b=3.0") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 2.5, 3.0)", + "GammaDist(x, 2.5, 3.0)") ); + + plot.AddDataset( Histogramm(GammaVariable(2.5, 4.5), probes, precision, + "GammaVariable a=2.5 b=4.5") ); + + plot.AddDataset( Gnuplot2dFunction("{/Symbol g}(x, 2.5, 4.5)", + "GammaDist(x, 2.5, 4.5)") ); + + gnuplots.AddPlot(plot); + } + + { + Gnuplot plot; + plot.SetTitle("ErlangVariable"); + plot.AppendExtra("set xrange [0:10]"); + plot.AppendExtra("ErlangDist(x,k,l) = x**(k-1) * 1/l**k * exp(-x/l) / (k-1)!"); + + plot.AppendExtra("set label 1 'Erlang(x,k,{/Symbol l}) = x^{k-1} e^{-x {/Symbol l}^{-1}} ( {/Symbol l}^k (k-1)! )^{-1}' at 0.7, 0.9"); + + plot.AddDataset( Histogramm(ErlangVariable(1, 1.0), probes, precision, + "ErlangVariable k=1 {/Symbol l}=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 1, 1.0)", + "ErlangDist(x, 1, 1.0)") ); + + plot.AddDataset( Histogramm(ErlangVariable(2, 1.0), probes, precision, + "ErlangVariable k=2 {/Symbol l}=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 2, 1.0)", + "ErlangDist(x, 2, 1.0)") ); + + plot.AddDataset( Histogramm(ErlangVariable(3, 1.0), probes, precision, + "ErlangVariable k=3 {/Symbol l}=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 3, 1.0)", + "ErlangDist(x, 3, 1.0)") ); + + plot.AddDataset( Histogramm(ErlangVariable(5, 1.0), probes, precision, + "ErlangVariable k=5 {/Symbol l}=1.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 5, 1.0)", + "ErlangDist(x, 5, 1.0)") ); + + plot.AddDataset( Histogramm(ErlangVariable(2, 2.0), probes, precision, + "ErlangVariable k=2 {/Symbol l}=2.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 2, 2.0)", + "ErlangDist(x, 2, 2.0)") ); + + plot.AddDataset( Histogramm(ErlangVariable(2, 3.0), probes, precision, + "ErlangVariable k=2 {/Symbol l}=3.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 2, 3.0)", + "ErlangDist(x, 2, 3.0)") ); + + plot.AddDataset( Histogramm(ErlangVariable(2, 5.0), probes, precision, + "ErlangVariable k=2 {/Symbol l}=5.0") ); + + plot.AddDataset( Gnuplot2dFunction("Erlang(x, 2, 5.0)", + "ErlangDist(x, 2, 5.0)") ); + + gnuplots.AddPlot(plot); + } + + gnuplots.GenerateOutput(std::cout); + + return 0; +} diff --git a/samples/wscript b/samples/wscript index deba5598e..0c081ee80 100644 --- a/samples/wscript +++ b/samples/wscript @@ -10,6 +10,9 @@ def build(bld): obj = bld.create_ns3_program('main-simulator') obj.source = 'main-simulator.cc' + obj = bld.create_ns3_program('main-random-variable') + obj.source = 'main-random-variable.cc' + obj = bld.create_ns3_program('main-packet-header', ['common', 'simulator']) obj.source = 'main-packet-header.cc' diff --git a/src/core/random-variable.cc b/src/core/random-variable.cc index 70be8a201..c8989d223 100644 --- a/src/core/random-variable.cc +++ b/src/core/random-variable.cc @@ -1218,6 +1218,230 @@ LogNormalVariable::LogNormalVariable (double mu, double sigma) : RandomVariable (LogNormalVariableImpl (mu, sigma)) {} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// GammaVariableImpl +class GammaVariableImpl : public RandomVariableBase +{ +public: + /** + * \param alpha alpha parameter of the gamma distribution + * \param beta beta parameter of the gamma distribution + */ + GammaVariableImpl (double alpha, double beta); + + /** + * \return A random value from this distribution + */ + virtual double GetValue (); + + /** + * \return A random value from the gamma distribution with parameters alpha + * and beta + */ + double GetValue(double alpha, double beta); + + virtual RandomVariableBase* Copy(void) const; + +private: + double m_alpha; + double m_beta; + NormalVariable m_normal; +}; + + +RandomVariableBase* GammaVariableImpl::Copy () const +{ + return new GammaVariableImpl (m_alpha, m_beta); +} + +GammaVariableImpl::GammaVariableImpl (double alpha, double beta) + : m_alpha(alpha), m_beta(beta) +{ +} + +double +GammaVariableImpl::GetValue () +{ + return GetValue(m_alpha, m_beta); +} + +/* + The code for the following generator functions was adapted from ns-2 + tools/ranvar.cc + + Originally the algorithm was devised by Marsaglia in 2000: + G. Marsaglia, W. W. Tsang: A simple method for gereating Gamma variables + ACM Transactions on mathematical software, Vol. 26, No. 3, Sept. 2000 + + The Gamma distribution density function has the form + + x^(alpha-1) * exp(-x/beta) + p(x; alpha, beta) = ---------------------------- + beta^alpha * Gamma(alpha) + + for x > 0. +*/ +double +GammaVariableImpl::GetValue (double alpha, double beta) +{ + if(!m_generator) + { + m_generator = new RngStream(); + } + + if (alpha < 1) + { + double u = m_generator->RandU01 (); + return GetValue(1.0 + alpha, beta) * pow (u, 1.0 / alpha); + } + + double x, v, u; + double d = alpha - 1.0 / 3.0; + double c = (1.0 / 3.0) / sqrt (d); + + while (1) + { + do + { + x = m_normal.GetValue (); + v = 1.0 + c * x; + } while (v <= 0); + + v = v * v * v; + u = m_generator->RandU01 (); + if (u < 1 - 0.0331 * x * x * x * x) + { + break; + } + if (log (u) < 0.5 * x * x + d * (1 - v + log (v))) + { + break; + } + } + + return beta * d * v; +} + +GammaVariable::GammaVariable () + : RandomVariable (GammaVariableImpl (1.0, 1.0)) +{ +} + +GammaVariable::GammaVariable (double alpha, double beta) + : RandomVariable (GammaVariableImpl (alpha, beta)) +{ +} + +double GammaVariable::GetValue(void) const +{ + return this->RandomVariable::GetValue (); +} + +double GammaVariable::GetValue(double alpha, double beta) const +{ + return ((GammaVariableImpl*)Peek())->GetValue(alpha, beta); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// ErlangVariableImpl + +class ErlangVariableImpl : public RandomVariableBase +{ +public: + /** + * \param k k parameter of the Erlang distribution + * \param lambda lambda parameter of the Erlang distribution + */ + ErlangVariableImpl (unsigned int k, double lambda); + + /** + * \return A random value from this distribution + */ + virtual double GetValue (); + + /** + * \return A random value from the Erlang distribution with parameters k and + * lambda. + */ + double GetValue(unsigned int k, double lambda); + + virtual RandomVariableBase* Copy(void) const; + +private: + unsigned int m_k; + double m_lambda; +}; + + +RandomVariableBase* ErlangVariableImpl::Copy () const +{ + return new ErlangVariableImpl (m_k, m_lambda); +} + +ErlangVariableImpl::ErlangVariableImpl (unsigned int k, double lambda) + : m_k(k), m_lambda(lambda) +{ +} + +double +ErlangVariableImpl::GetValue () +{ + return GetValue(m_k, m_lambda); +} + +/* + The code for the following generator functions was adapted from ns-2 + tools/ranvar.cc + + The Erlang distribution density function has the form + + x^(k-1) * exp(-x/lambda) + p(x; k, lambda) = --------------------------- + lambda^k * (k-1)! + + for x > 0. +*/ +double +ErlangVariableImpl::GetValue (unsigned int k, double lambda) +{ + if(!m_generator) + { + m_generator = new RngStream(); + } + + ExponentialVariable exponential(lambda); + + double result = 0; + for (unsigned int i = 0; i < k; ++i) + { + result += exponential.GetValue(); + } + + return result; +} + +ErlangVariable::ErlangVariable () + : RandomVariable (ErlangVariableImpl (1, 1.0)) +{ +} + +ErlangVariable::ErlangVariable (unsigned int k, double lambda) + : RandomVariable (ErlangVariableImpl (k, lambda)) +{ +} + +double ErlangVariable::GetValue(void) const +{ + return this->RandomVariable::GetValue (); +} + +double ErlangVariable::GetValue(unsigned int k, double lambda) const +{ + return ((ErlangVariableImpl*)Peek())->GetValue(k, lambda); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // TriangularVariableImpl methods diff --git a/src/core/random-variable.h b/src/core/random-variable.h index fc48ddf7a..a637726d4 100644 --- a/src/core/random-variable.h +++ b/src/core/random-variable.h @@ -36,7 +36,7 @@ * */ -namespace ns3{ +namespace ns3 { class RandomVariableBase; @@ -422,7 +422,7 @@ public: * value and a shape (alpha) parameter of 1.5. * \param m mean value of the distribution */ - WeibullVariable(double m) ; + WeibullVariable(double m) ; /** * Constructs a weibull random variable with the specified mean @@ -560,7 +560,6 @@ public: explicit DeterministicVariable(double* d, uint32_t c); }; - /** * \brief Log-normal Distributed random var * \ingroup randomvariable @@ -593,6 +592,108 @@ public: LogNormalVariable (double mu, double sigma); }; +/** + * \brief Gamma Distributed Random Variable + * \ingroup randomvariable + * + * GammaVariable defines a random variable with gamma distribution. + * + * This class supports the creation of objects that return random numbers + * from a fixed gamma distribution. It also supports the generation of + * single random numbers from various gamma distributions. + * + * The probability density function is defined over the interval [0,+inf) as: + * \f$ x^{\alpha-1} \frac{e^{-\frac{x}{\beta}}}{\beta^\alpha \Gamma(\alpha)}\f$ + * where \f$ mean = \alpha\beta \f$ and + * \f$ variance = \alpha \beta^2\f$ + */ +class GammaVariable : public RandomVariable +{ +public: + /** + * Constructs a gamma random variable with alpha = 1.0 and beta = 1.0 + */ + GammaVariable (); + + /** + * \param alpha alpha parameter of the gamma distribution + * \param beta beta parameter of the gamma distribution + */ + GammaVariable (double alpha, double beta); + + /** + * \brief call RandomVariable::GetValue + * \return A floating point random value + * + * Note: we have to re-implement this method here because the method is + * overloaded below for the two-argument variant and the c++ name resolution + * rules don't work well with overloads split between parent and child + * classes. + */ + double GetValue (void) const; + + /** + * \brief Returns a gamma random distributed double with parameters alpha and beta. + * \param alpha alpha parameter of the gamma distribution + * \param beta beta parameter of the gamma distribution + * \return A floating point random value + */ + double GetValue(double alpha, double beta) const; +}; + +/** + * \brief Erlang Distributed Random Variable + * \ingroup randomvariable + * + * ErlangVariable defines a random variable with Erlang distribution. + * + * The Erlang distribution is a special case of the Gamma distribution where k + * (= alpha) is a non-negative integer. Erlang distributed variables can be + * generated using a much faster algorithm than gamma variables. + * + * This class supports the creation of objects that return random numbers from + * a fixed Erlang distribution. It also supports the generation of single + * random numbers from various Erlang distributions. + * + * The probability density function is defined over the interval [0,+inf) as: + * \f$ \frac{x^{k-1} e^{-\frac{x}{\lambda}}}{\lambda^k (k-1)!}\f$ + * where \f$ mean = k \lambda \f$ and + * \f$ variance = k \lambda^2\f$ + */ +class ErlangVariable : public RandomVariable +{ +public: + /** + * Constructs an Erlang random variable with k = 1 and lambda = 1.0 + */ + ErlangVariable (); + + /** + * \param k k parameter of the Erlang distribution. Must be a non-negative integer. + * \param lambda lambda parameter of the Erlang distribution + */ + ErlangVariable (unsigned int k, double lambda); + + /** + * \brief call RandomVariable::GetValue + * \return A floating point random value + * + * Note: we have to re-implement this method here because the method is + * overloaded below for the two-argument variant and the c++ name resolution + * rules don't work well with overloads split between parent and child + * classes. + */ + double GetValue (void) const; + + /** + * \brief Returns an Erlang random distributed double with parameters k and lambda. + * \param k k parameter of the Erlang distribution. Must be a non-negative integer. + * \param lambda lambda parameter of the Erlang distribution + * \return A floating point random value + */ + double GetValue(unsigned int k, double lambda) const; +}; + /** * \brief Triangularly Distributed random var * \ingroup randomvariable From c9c9a434d46c221e17eb6cb565c48b35348b4044 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 10:27:55 +0200 Subject: [PATCH 17/49] bug 544: TypeId::AddTraceSource should assert on duplicate trace source name --- src/core/type-id.cc | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/core/type-id.cc b/src/core/type-id.cc index 90d400c05..2c2f0740f 100644 --- a/src/core/type-id.cc +++ b/src/core/type-id.cc @@ -71,6 +71,8 @@ public: bool MustHideFromDocumentation (uint16_t uid) const; private: + bool HasTraceSource (uint16_t uid, std::string name); + struct AttributeInformation { std::string name; std::string help; @@ -310,6 +312,33 @@ IidManager::GetAttributeChecker (uint16_t uid, uint32_t i) const return information->attributes[i].checker; } +bool +IidManager::HasTraceSource (uint16_t uid, + std::string name) +{ + struct IidInformation *information = LookupInformation (uid); + while (true) + { + for (std::vector::const_iterator i = information->traceSources.begin (); + i != information->traceSources.end (); ++i) + { + if (i->name == name) + { + return true; + } + } + struct IidInformation *parent = LookupInformation (information->parent); + if (parent == information) + { + // top of inheritance tree + return false; + } + // check parent + information = parent; + } + return false; +} + void IidManager::AddTraceSource (uint16_t uid, std::string name, @@ -317,6 +346,11 @@ IidManager::AddTraceSource (uint16_t uid, ns3::Ptr accessor) { struct IidInformation *information = LookupInformation (uid); + if (HasTraceSource (uid, name)) + { + NS_FATAL_ERROR ("Trace source \"" << name << "\" already registered on tid=\"" << + information->name << "\""); + } struct TraceSourceInformation source; source.name = name; source.help = help; From 5cdcf130a59b675afe70c4b3ca09486b0d105840 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 10:55:42 +0200 Subject: [PATCH 18/49] rename attribute from Position to PositionAllocator to avoid conflict with Position attribute defined in ns3::MobilityModel --- src/mobility/random-waypoint-mobility-model.cc | 2 +- utils/python-unit-tests.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mobility/random-waypoint-mobility-model.cc b/src/mobility/random-waypoint-mobility-model.cc index 5188fff82..bff248676 100644 --- a/src/mobility/random-waypoint-mobility-model.cc +++ b/src/mobility/random-waypoint-mobility-model.cc @@ -45,7 +45,7 @@ RandomWaypointMobilityModel::GetTypeId (void) RandomVariableValue (ConstantVariable (2.0)), MakeRandomVariableAccessor (&RandomWaypointMobilityModel::m_pause), MakeRandomVariableChecker ()) - .AddAttribute ("Position", + .AddAttribute ("PositionAllocator", "The position model used to pick a destination point.", PointerValue (), MakePointerAccessor (&RandomWaypointMobilityModel::m_position), diff --git a/utils/python-unit-tests.py b/utils/python-unit-tests.py index 00713e8d9..a08f668fe 100644 --- a/utils/python-unit-tests.py +++ b/utils/python-unit-tests.py @@ -97,14 +97,14 @@ class TestSimulator(unittest.TestCase): ## -- object pointer values mobility = ns3.RandomWaypointMobilityModel() ptr = ns3.PointerValue() - mobility.GetAttribute("Position", ptr) + mobility.GetAttribute("PositionAllocator", ptr) self.assertEqual(ptr.GetObject(), None) pos = ns3.ListPositionAllocator() - mobility.SetAttribute("Position", ns3.PointerValue(pos)) + mobility.SetAttribute("PositionAllocator", ns3.PointerValue(pos)) ptr = ns3.PointerValue() - mobility.GetAttribute("Position", ptr) + mobility.GetAttribute("PositionAllocator", ptr) self.assert_(ptr.GetObject() is not None) def testIdentity(self): From d2631f6102d68767ad3b922f413bc7e6a680248d Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 10:55:56 +0200 Subject: [PATCH 19/49] detect duplicate attributes, even in parent classes --- src/core/type-id.cc | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/core/type-id.cc b/src/core/type-id.cc index 2c2f0740f..dcab76ecb 100644 --- a/src/core/type-id.cc +++ b/src/core/type-id.cc @@ -72,6 +72,7 @@ public: private: bool HasTraceSource (uint16_t uid, std::string name); + bool HasAttribute (uint16_t uid, std::string name); struct AttributeInformation { std::string name; @@ -233,6 +234,33 @@ IidManager::GetRegistered (uint32_t i) const return i + 1; } +bool +IidManager::HasAttribute (uint16_t uid, + std::string name) +{ + struct IidInformation *information = LookupInformation (uid); + while (true) + { + for (std::vector::const_iterator i = information->attributes.begin (); + i != information->attributes.end (); ++i) + { + if (i->name == name) + { + return true; + } + } + struct IidInformation *parent = LookupInformation (information->parent); + if (parent == information) + { + // top of inheritance tree + return false; + } + // check parent + information = parent; + } + return false; +} + void IidManager::AddAttribute (uint16_t uid, std::string name, @@ -243,14 +271,10 @@ IidManager::AddAttribute (uint16_t uid, ns3::Ptr checker) { struct IidInformation *information = LookupInformation (uid); - for (std::vector::const_iterator j = information->attributes.begin (); - j != information->attributes.end (); j++) + if (HasAttribute (uid, name)) { - if (j->name == name) - { - NS_FATAL_ERROR ("Registered the same attribute twice name=\""<name<<"\""); - return; - } + NS_FATAL_ERROR ("Attribute \"" << name << "\" already registered on tid=\"" << + information->name << "\""); } struct AttributeInformation param; param.name = name; From 306fe227693092f725eab55ce8cb3a0ee3ab29d6 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 11:00:13 +0200 Subject: [PATCH 20/49] bug 542: invalid trace paths --- examples/wifi-ap.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/wifi-ap.cc b/examples/wifi-ap.cc index 6bd6c74d1..e5cc5d84e 100644 --- a/examples/wifi-ap.cc +++ b/examples/wifi-ap.cc @@ -32,14 +32,14 @@ using namespace ns3; void -DevTxTrace (std::string context, Ptr p, Mac48Address address) +DevTxTrace (std::string context, Ptr p) { - std::cout << " TX to=" << address << " p: " << *p << std::endl; + std::cout << " TX p: " << *p << std::endl; } void -DevRxTrace (std::string context, Ptr p, Mac48Address address) +DevRxTrace (std::string context, Ptr p) { - std::cout << " RX from=" << address << " p: " << *p << std::endl; + std::cout << " RX p: " << *p << std::endl; } void PhyRxOkTrace (std::string context, Ptr packet, double snr, WifiMode mode, enum WifiPreamble preamble) @@ -168,8 +168,8 @@ int main (int argc, char *argv[]) Simulator::Stop (Seconds (44.0)); - Config::Connect ("/NodeList/*/DeviceList/*/Tx", MakeCallback (&DevTxTrace)); - Config::Connect ("/NodeList/*/DeviceList/*/Rx", MakeCallback (&DevRxTrace)); + Config::Connect ("/NodeList/*/DeviceList/*/Mac/MacTx", MakeCallback (&DevTxTrace)); + Config::Connect ("/NodeList/*/DeviceList/*/Mac/MacRx", MakeCallback (&DevRxTrace)); Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/RxOk", MakeCallback (&PhyRxOkTrace)); Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/RxError", MakeCallback (&PhyRxErrorTrace)); Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/Tx", MakeCallback (&PhyTxTrace)); From 1177cadc35437b7035e014b40d8c47573c50b127 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 11:10:17 +0200 Subject: [PATCH 21/49] bug 498: Object Name Service Obsoletes Legacy Naming --- src/devices/bridge/bridge-channel.cc | 2 +- src/devices/bridge/bridge-net-device.cc | 33 ++++++----------- src/devices/bridge/bridge-net-device.h | 3 -- src/devices/csma/csma-channel.cc | 2 +- src/devices/csma/csma-net-device.cc | 36 +------------------ src/devices/csma/csma-net-device.h | 7 ---- src/devices/emu/emu-net-device.cc | 20 +---------- src/devices/emu/emu-net-device.h | 8 ----- .../point-to-point/point-to-point-channel.cc | 4 +-- .../point-to-point/point-to-point-channel.h | 2 +- .../point-to-point-net-device.cc | 13 ------- .../point-to-point-net-device.h | 7 ---- src/devices/tap-bridge/tap-bridge.cc | 14 -------- src/devices/tap-bridge/tap-bridge.h | 8 ----- src/devices/wifi/wifi-net-device.cc | 10 ------ src/devices/wifi/wifi-net-device.h | 3 -- src/node/channel.cc | 20 ----------- src/node/channel.h | 8 +---- src/node/net-device.h | 8 ----- src/node/node.cc | 7 ++-- src/node/simple-net-device.cc | 11 ------ src/node/simple-net-device.h | 3 -- 22 files changed, 21 insertions(+), 208 deletions(-) diff --git a/src/devices/bridge/bridge-channel.cc b/src/devices/bridge/bridge-channel.cc index e5eac7a54..b8b8b6bca 100644 --- a/src/devices/bridge/bridge-channel.cc +++ b/src/devices/bridge/bridge-channel.cc @@ -34,7 +34,7 @@ BridgeChannel::GetTypeId (void) } BridgeChannel::BridgeChannel () - : Channel ("BridgeChannel") + : Channel () { NS_LOG_FUNCTION_NOARGS (); } diff --git a/src/devices/bridge/bridge-net-device.cc b/src/devices/bridge/bridge-net-device.cc index b4a376b8c..03d7778eb 100644 --- a/src/devices/bridge/bridge-net-device.cc +++ b/src/devices/bridge/bridge-net-device.cc @@ -53,7 +53,6 @@ BridgeNetDevice::GetTypeId (void) BridgeNetDevice::BridgeNetDevice () : m_node (0), - m_name (""), m_ifIndex (0), m_mtu (0xffff) { @@ -128,7 +127,7 @@ BridgeNetDevice::ForwardUnicast (Ptr incomingPort, Ptr uint16_t protocol, Mac48Address src, Mac48Address dst) { NS_LOG_FUNCTION_NOARGS (); - NS_LOG_DEBUG ("LearningBridgeForward (incomingPort=" << incomingPort->GetName () + NS_LOG_DEBUG ("LearningBridgeForward (incomingPort=" << incomingPort->GetInstanceTypeId ().GetName () << ", packet=" << packet << ", protocol="< incomingPort, Ptr Ptr outPort = GetLearnedState (dst); if (outPort != NULL && outPort != incomingPort) { - NS_LOG_LOGIC ("Learning bridge state says to use port `" << outPort->GetName () << "'"); + NS_LOG_LOGIC ("Learning bridge state says to use port `" << outPort->GetInstanceTypeId ().GetName () << "'"); outPort->SendFrom (packet->Copy (), src, dst, protocol); } else @@ -148,8 +147,9 @@ BridgeNetDevice::ForwardUnicast (Ptr incomingPort, Ptr Ptr port = *iter; if (port != incomingPort) { - NS_LOG_LOGIC ("LearningBridgeForward (" << src << " => " << dst << "): " << incomingPort->GetName () - << " --> " << port->GetName () + NS_LOG_LOGIC ("LearningBridgeForward (" << src << " => " << dst << "): " + << incomingPort->GetInstanceTypeId ().GetName () + << " --> " << port->GetInstanceTypeId ().GetName () << " (UID " << packet->GetUid () << ")."); port->SendFrom (packet->Copy (), src, dst, protocol); } @@ -162,7 +162,7 @@ BridgeNetDevice::ForwardBroadcast (Ptr incomingPort, Ptr incomingPort, Ptr port = *iter; if (port != incomingPort) { - NS_LOG_LOGIC ("LearningBridgeForward (" << src << " => " << dst << "): " << incomingPort->GetName () - << " --> " << port->GetName () + NS_LOG_LOGIC ("LearningBridgeForward (" << src << " => " << dst << "): " + << incomingPort->GetInstanceTypeId ().GetName () + << " --> " << port->GetInstanceTypeId ().GetName () << " (UID " << packet->GetUid () << ")."); port->SendFrom (packet->Copy (), src, dst, protocol); } @@ -249,27 +250,13 @@ BridgeNetDevice::AddBridgePort (Ptr bridgePort) m_address = Mac48Address::ConvertFrom (bridgePort->GetAddress ()); } - NS_LOG_DEBUG ("RegisterProtocolHandler for " << bridgePort->GetName ()); + NS_LOG_DEBUG ("RegisterProtocolHandler for " << bridgePort->GetInstanceTypeId ().GetName ()); m_node->RegisterProtocolHandler (MakeCallback (&BridgeNetDevice::ReceiveFromDevice, this), 0, bridgePort, true); m_ports.push_back (bridgePort); m_channel->AddChannel (bridgePort->GetChannel ()); } -void -BridgeNetDevice::SetName(const std::string name) -{ - NS_LOG_FUNCTION_NOARGS (); - m_name = name; -} - -std::string -BridgeNetDevice::GetName(void) const -{ - NS_LOG_FUNCTION_NOARGS (); - return m_name; -} - void BridgeNetDevice::SetIfIndex(const uint32_t index) { diff --git a/src/devices/bridge/bridge-net-device.h b/src/devices/bridge/bridge-net-device.h index 4af590c97..02be49f37 100644 --- a/src/devices/bridge/bridge-net-device.h +++ b/src/devices/bridge/bridge-net-device.h @@ -88,8 +88,6 @@ public: Ptr GetBridgePort (uint32_t n) const; // inherited from NetDevice base class. - virtual void SetName(const std::string name); - virtual std::string GetName(void) const; virtual void SetIfIndex(const uint32_t index); virtual uint32_t GetIfIndex(void) const; virtual Ptr GetChannel (void) const; @@ -140,7 +138,6 @@ private: std::map m_learnState; Ptr m_node; Ptr m_channel; - std::string m_name; std::vector< Ptr > m_ports; uint32_t m_ifIndex; uint16_t m_mtu; diff --git a/src/devices/csma/csma-channel.cc b/src/devices/csma/csma-channel.cc index ab2a83bf3..20b870db1 100644 --- a/src/devices/csma/csma-channel.cc +++ b/src/devices/csma/csma-channel.cc @@ -51,7 +51,7 @@ CsmaChannel::GetTypeId (void) CsmaChannel::CsmaChannel () : - Channel ("Csma Channel") + Channel () { NS_LOG_FUNCTION_NOARGS (); m_state = IDLE; diff --git a/src/devices/csma/csma-net-device.cc b/src/devices/csma/csma-net-device.cc index f9742c649..dc953e7b6 100644 --- a/src/devices/csma/csma-net-device.cc +++ b/src/devices/csma/csma-net-device.cc @@ -154,8 +154,7 @@ CsmaNetDevice::GetTypeId (void) } CsmaNetDevice::CsmaNetDevice () - : m_name (""), - m_linkUp (false) + : m_linkUp (false) { NS_LOG_FUNCTION (this); m_txMachineState = READY; @@ -873,20 +872,6 @@ CsmaNetDevice::NotifyLinkUp (void) } } - void -CsmaNetDevice::SetName (const std::string name) -{ - NS_LOG_FUNCTION (name); - m_name = name; -} - - std::string -CsmaNetDevice::GetName (void) const -{ - NS_LOG_FUNCTION_NOARGS (); - return m_name; -} - void CsmaNetDevice::SetIfIndex (const uint32_t index) { @@ -1054,25 +1039,6 @@ CsmaNetDevice::SetNode (Ptr node) NS_LOG_FUNCTION (node); m_node = node; - int count = -1; - if (m_name.size () == 0) - { - for (uint32_t i = 0; i < node->GetNDevices (); i++) - { - Ptr dev = node->GetDevice (i); - if (dynamic_cast (PeekPointer (dev))) - { - count++; - if (dev == this) - { - break; - } - } - } - std::ostringstream s; - s << "eth" << count; - m_name = s.str (); - } } bool diff --git a/src/devices/csma/csma-net-device.h b/src/devices/csma/csma-net-device.h index 2213e1390..7dc1f9c55 100644 --- a/src/devices/csma/csma-net-device.h +++ b/src/devices/csma/csma-net-device.h @@ -305,8 +305,6 @@ public: // // The following methods are inherited from NetDevice base class. // - virtual void SetName (const std::string name); - virtual std::string GetName (void) const; virtual void SetIfIndex (const uint32_t index); virtual uint32_t GetIfIndex (void) const; virtual Ptr GetChannel (void) const; @@ -807,11 +805,6 @@ private: */ uint32_t m_ifIndex; - /** - * The human readable name of this device. - */ - std::string m_name; - /** * Flag indicating whether or not the link is up. In this case, * whether or not the device is connected to a channel. diff --git a/src/devices/emu/emu-net-device.cc b/src/devices/emu/emu-net-device.cc index 0a987850e..7d3e451eb 100644 --- a/src/devices/emu/emu-net-device.cc +++ b/src/devices/emu/emu-net-device.cc @@ -68,11 +68,6 @@ EmuNetDevice::GetTypeId (void) Mac48AddressValue (Mac48Address ("ff:ff:ff:ff:ff:ff")), MakeMac48AddressAccessor (&EmuNetDevice::m_address), MakeMac48AddressChecker ()) - .AddAttribute ("DeviceName", - "The name of the underlying real device (e.g. eth1).", - StringValue ("eth1"), - MakeStringAccessor (&EmuNetDevice::m_deviceName), - MakeStringChecker ()) .AddAttribute ("Start", "The simulation time at which to spin up the device thread.", TimeValue (Seconds (0.)), @@ -173,8 +168,7 @@ EmuNetDevice::EmuNetDevice () m_sock (-1), m_readThread (0), m_ifIndex (std::numeric_limits::max ()), // absurdly large value - m_sll_ifindex (-1), - m_name ("Emu NetDevice") + m_sll_ifindex (-1) { NS_LOG_FUNCTION (this); Start (m_tStart); @@ -843,18 +837,6 @@ EmuNetDevice::NotifyLinkUp (void) } } -void -EmuNetDevice::SetName(const std::string name) -{ - m_name = name; -} - -std::string -EmuNetDevice::GetName(void) const -{ - return m_name; -} - void EmuNetDevice::SetIfIndex(const uint32_t index) { diff --git a/src/devices/emu/emu-net-device.h b/src/devices/emu/emu-net-device.h index 02947cb53..189a81833 100644 --- a/src/devices/emu/emu-net-device.h +++ b/src/devices/emu/emu-net-device.h @@ -105,9 +105,6 @@ public: // // Pure virtual methods inherited from NetDevice we must implement. // - virtual void SetName(const std::string name); - virtual std::string GetName(void) const; - virtual void SetIfIndex(const uint32_t index); virtual uint32_t GetIfIndex(void) const; @@ -449,11 +446,6 @@ private: */ int32_t m_sll_ifindex; - /** - * The human readable name of this device. - */ - std::string m_name; - /** * Flag indicating whether or not the link is up. In this case, * whether or not the device is connected to a channel. diff --git a/src/devices/point-to-point/point-to-point-channel.cc b/src/devices/point-to-point/point-to-point-channel.cc index b94add35c..6fa1b7bee 100644 --- a/src/devices/point-to-point/point-to-point-channel.cc +++ b/src/devices/point-to-point/point-to-point-channel.cc @@ -43,11 +43,11 @@ PointToPointChannel::GetTypeId (void) } // -// By default, you get a channel with the name "PointToPoint Channel" that +// By default, you get a channel that // has an "infitely" fast transmission speed and zero delay. PointToPointChannel::PointToPointChannel() : - Channel ("PointToPoint Channel"), + Channel (), m_delay (Seconds (0.)), m_nDevices (0) { diff --git a/src/devices/point-to-point/point-to-point-channel.h b/src/devices/point-to-point/point-to-point-channel.h index b31a37ae7..9830e048f 100644 --- a/src/devices/point-to-point/point-to-point-channel.h +++ b/src/devices/point-to-point/point-to-point-channel.h @@ -50,7 +50,7 @@ public: /** * \brief Create a PointToPointChannel * - * By default, you get a channel with the name "PointToPoint Channel" that + * By default, you get a channel that * has zero transmission delay. */ PointToPointChannel (); diff --git a/src/devices/point-to-point/point-to-point-net-device.cc b/src/devices/point-to-point/point-to-point-net-device.cc index 856ba6852..609441cad 100644 --- a/src/devices/point-to-point/point-to-point-net-device.cc +++ b/src/devices/point-to-point/point-to-point-net-device.cc @@ -148,7 +148,6 @@ PointToPointNetDevice::PointToPointNetDevice () : m_txMachineState (READY), m_channel (0), - m_name (""), m_linkUp (false), m_currentPkt (0) { @@ -373,18 +372,6 @@ PointToPointNetDevice::NotifyLinkUp (void) } } - void -PointToPointNetDevice::SetName(const std::string name) -{ - m_name = name; -} - - std::string -PointToPointNetDevice::GetName(void) const -{ - return m_name; -} - void PointToPointNetDevice::SetIfIndex(const uint32_t index) { diff --git a/src/devices/point-to-point/point-to-point-net-device.h b/src/devices/point-to-point/point-to-point-net-device.h index 5af6c9c66..956b6a356 100644 --- a/src/devices/point-to-point/point-to-point-net-device.h +++ b/src/devices/point-to-point/point-to-point-net-device.h @@ -226,12 +226,6 @@ public: */ uint16_t GetFrameSize (void) const; -// -// Pure virtual methods inherited from NetDevice we must implement. -// - virtual void SetName(const std::string name); - virtual std::string GetName(void) const; - virtual void SetIfIndex(const uint32_t index); virtual uint32_t GetIfIndex(void) const; @@ -531,7 +525,6 @@ private: NetDevice::ReceiveCallback m_rxCallback; NetDevice::PromiscReceiveCallback m_promiscCallback; uint32_t m_ifIndex; - std::string m_name; bool m_linkUp; Callback m_linkChangeCallback; diff --git a/src/devices/tap-bridge/tap-bridge.cc b/src/devices/tap-bridge/tap-bridge.cc index fa14796a9..9c99c21fb 100644 --- a/src/devices/tap-bridge/tap-bridge.cc +++ b/src/devices/tap-bridge/tap-bridge.cc @@ -957,20 +957,6 @@ TapBridge::ReceiveFromBridgedDevice ( NS_ABORT_MSG_IF (bytesWritten != p->GetSize (), "TapBridge::ReceiveFromBridgedDevice(): Write error."); } -void -TapBridge::SetName(const std::string name) -{ - NS_LOG_FUNCTION_NOARGS (); - m_name = name; -} - -std::string -TapBridge::GetName(void) const -{ - NS_LOG_FUNCTION_NOARGS (); - return m_name; -} - void TapBridge::SetIfIndex(const uint32_t index) { diff --git a/src/devices/tap-bridge/tap-bridge.h b/src/devices/tap-bridge/tap-bridge.h index 62d6a1350..ac2fb2a2d 100644 --- a/src/devices/tap-bridge/tap-bridge.h +++ b/src/devices/tap-bridge/tap-bridge.h @@ -173,8 +173,6 @@ public: // The following methods are inherited from NetDevice base class and are // documented there. // - virtual void SetName(const std::string name); - virtual std::string GetName(void) const; virtual void SetIfIndex(const uint32_t index); virtual uint32_t GetIfIndex(void) const; virtual Ptr GetChannel (void) const; @@ -318,12 +316,6 @@ private: */ Ptr m_node; - /** - * \internal - * - * A possible name for the (ghost) Node to which we are connected. - */ - std::string m_name; /** * \internal diff --git a/src/devices/wifi/wifi-net-device.cc b/src/devices/wifi/wifi-net-device.cc index 5f67c2224..d7dccc85d 100644 --- a/src/devices/wifi/wifi-net-device.cc +++ b/src/devices/wifi/wifi-net-device.cc @@ -136,16 +136,6 @@ WifiNetDevice::GetRemoteStationManager (void) const return m_stationManager; } -void -WifiNetDevice::SetName(const std::string name) -{ - m_name = name; -} -std::string -WifiNetDevice::GetName(void) const -{ - return m_name; -} void WifiNetDevice::SetIfIndex(const uint32_t index) { diff --git a/src/devices/wifi/wifi-net-device.h b/src/devices/wifi/wifi-net-device.h index 2bee1d2f9..d5aae5797 100644 --- a/src/devices/wifi/wifi-net-device.h +++ b/src/devices/wifi/wifi-net-device.h @@ -75,8 +75,6 @@ public: // inherited from NetDevice base class. - virtual void SetName(const std::string name); - virtual std::string GetName(void) const; virtual void SetIfIndex(const uint32_t index); virtual uint32_t GetIfIndex(void) const; virtual Ptr GetChannel (void) const; @@ -123,7 +121,6 @@ private: TracedCallback, Mac48Address> m_txLogger; uint32_t m_ifIndex; - std::string m_name; bool m_linkUp; Callback m_linkChange; mutable uint16_t m_mtu; diff --git a/src/node/channel.cc b/src/node/channel.cc index 6f23bad49..e85503c66 100644 --- a/src/node/channel.cc +++ b/src/node/channel.cc @@ -35,34 +35,14 @@ Channel::GetTypeId (void) } Channel::Channel () - : m_name("Channel") { NS_LOG_FUNCTION_NOARGS (); } -Channel::Channel (std::string name) - : m_name(name) -{ - NS_LOG_FUNCTION (this << name); -} Channel::~Channel () { NS_LOG_FUNCTION_NOARGS (); } - void -Channel::SetName(std::string name) -{ - NS_LOG_FUNCTION (this << name); - m_name = name; -} - - std::string -Channel::GetName(void) -{ - NS_LOG_FUNCTION_NOARGS (); - return m_name; -} - } // namespace ns3 diff --git a/src/node/channel.h b/src/node/channel.h index 1e4a0cd6a..68f8d3175 100644 --- a/src/node/channel.h +++ b/src/node/channel.h @@ -43,12 +43,8 @@ public: static TypeId GetTypeId (void); Channel (); - Channel (std::string name); virtual ~Channel (); - void SetName(std::string); - std::string GetName(void); - /** * \returns the number of NetDevices connected to this Channel. * @@ -63,10 +59,8 @@ public: */ virtual Ptr GetDevice (uint32_t i) const = 0; -private: - std::string m_name; }; -}; // namespace ns3 +} // namespace ns3 #endif /* NS3_CHANNEL_H */ diff --git a/src/node/net-device.h b/src/node/net-device.h index ced6c9dff..dcdaa6d75 100644 --- a/src/node/net-device.h +++ b/src/node/net-device.h @@ -67,14 +67,6 @@ public: static TypeId GetTypeId (void); virtual ~NetDevice(); - /** - * \param name name of the device (e.g. "eth0") - */ - virtual void SetName(const std::string name) = 0; - /** - * \return name name of the device (e.g. "eth0") - */ - virtual std::string GetName(void) const = 0; /** * \param index ifIndex of the device */ diff --git a/src/node/node.cc b/src/node/node.cc index 9cecbab14..199490484 100644 --- a/src/node/node.cc +++ b/src/node/node.cc @@ -225,7 +225,7 @@ bool Node::PromiscReceiveFromDevice (Ptr device, Ptr packet, uint16_t protocol, const Address &from, const Address &to, NetDevice::PacketType packetType) { - NS_LOG_FUNCTION(device->GetName ()); + NS_LOG_FUNCTION(this); return ReceiveFromDevice (device, packet, protocol, from, to, packetType, true); } @@ -233,7 +233,7 @@ bool Node::NonPromiscReceiveFromDevice (Ptr device, Ptr packet, uint16_t protocol, const Address &from) { - NS_LOG_FUNCTION(device->GetName ()); + NS_LOG_FUNCTION(this); return ReceiveFromDevice (device, packet, protocol, from, from, NetDevice::PacketType (0), false); } @@ -242,8 +242,7 @@ Node::ReceiveFromDevice (Ptr device, Ptr packet, uint16 const Address &from, const Address &to, NetDevice::PacketType packetType, bool promiscuous) { NS_LOG_DEBUG("Node " << GetId () << " ReceiveFromDevice: dev " - << device->GetIfIndex () << " (" - << device->GetName () << " type " << device->GetInstanceTypeId ().GetName () + << device->GetIfIndex () << " (type=" << device->GetInstanceTypeId ().GetName () << ") Packet UID " << packet->GetUid ()); bool found = false; diff --git a/src/node/simple-net-device.cc b/src/node/simple-net-device.cc index d13bd53fe..d996d5341 100644 --- a/src/node/simple-net-device.cc +++ b/src/node/simple-net-device.cc @@ -38,7 +38,6 @@ SimpleNetDevice::SimpleNetDevice () : m_channel (0), m_node (0), m_mtu (0xffff), - m_name (""), m_ifIndex (0) {} @@ -83,16 +82,6 @@ SimpleNetDevice::SetAddress (Mac48Address address) m_address = address; } -void -SimpleNetDevice::SetName(const std::string name) -{ - m_name = name; -} -std::string -SimpleNetDevice::GetName(void) const -{ - return m_name; -} void SimpleNetDevice::SetIfIndex(const uint32_t index) { diff --git a/src/node/simple-net-device.h b/src/node/simple-net-device.h index 1e1f15323..7840887cf 100644 --- a/src/node/simple-net-device.h +++ b/src/node/simple-net-device.h @@ -46,8 +46,6 @@ public: void SetAddress (Mac48Address address); // inherited from NetDevice base class. - virtual void SetName(const std::string name); - virtual std::string GetName(void) const; virtual void SetIfIndex(const uint32_t index); virtual uint32_t GetIfIndex(void) const; virtual Ptr GetChannel (void) const; @@ -82,7 +80,6 @@ private: NetDevice::PromiscReceiveCallback m_promiscCallback; Ptr m_node; uint16_t m_mtu; - std::string m_name; uint32_t m_ifIndex; Mac48Address m_address; }; From 48d617eb9dba3f0c9a25e2aadad94689ab3c8ea2 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 16 Apr 2009 11:10:27 +0200 Subject: [PATCH 22/49] rescan python --- bindings/python/ns3_module_bridge.py | 10 ----- bindings/python/ns3_module_core.py | 44 ++++++++++++++++++++ bindings/python/ns3_module_csma.py | 10 ----- bindings/python/ns3_module_emu.py | 10 ----- bindings/python/ns3_module_node.py | 30 ------------- bindings/python/ns3_module_point_to_point.py | 10 ----- bindings/python/ns3_module_tap_bridge.py | 10 ----- bindings/python/ns3_module_wifi.py | 10 ----- 8 files changed, 44 insertions(+), 90 deletions(-) diff --git a/bindings/python/ns3_module_bridge.py b/bindings/python/ns3_module_bridge.py index 013863812..647193174 100644 --- a/bindings/python/ns3_module_bridge.py +++ b/bindings/python/ns3_module_bridge.py @@ -103,16 +103,6 @@ def register_Ns3BridgeNetDevice_methods(root_module, cls): 'ns3::Ptr< ns3::NetDevice >', [param('uint32_t', 'n')], is_const=True) - ## bridge-net-device.h: void ns3::BridgeNetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## bridge-net-device.h: std::string ns3::BridgeNetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## bridge-net-device.h: void ns3::BridgeNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/ns3_module_core.py b/bindings/python/ns3_module_core.py index 82c9a71c1..e108eba13 100644 --- a/bindings/python/ns3_module_core.py +++ b/bindings/python/ns3_module_core.py @@ -115,8 +115,12 @@ def register_types(module): module.add_class('EnumChecker', parent=root_module['ns3::AttributeChecker']) ## enum.h: ns3::EnumValue [class] module.add_class('EnumValue', parent=root_module['ns3::AttributeValue']) + ## random-variable.h: ns3::ErlangVariable [class] + module.add_class('ErlangVariable', parent=root_module['ns3::RandomVariable']) ## random-variable.h: ns3::ExponentialVariable [class] module.add_class('ExponentialVariable', parent=root_module['ns3::RandomVariable']) + ## random-variable.h: ns3::GammaVariable [class] + module.add_class('GammaVariable', parent=root_module['ns3::RandomVariable']) ## random-variable.h: ns3::IntEmpiricalVariable [class] module.add_class('IntEmpiricalVariable', parent=root_module['ns3::EmpiricalVariable']) ## integer.h: ns3::IntegerValue [class] @@ -261,7 +265,9 @@ def register_methods(root_module): register_Ns3EmptyAttributeValue_methods(root_module, root_module['ns3::EmptyAttributeValue']) register_Ns3EnumChecker_methods(root_module, root_module['ns3::EnumChecker']) register_Ns3EnumValue_methods(root_module, root_module['ns3::EnumValue']) + register_Ns3ErlangVariable_methods(root_module, root_module['ns3::ErlangVariable']) register_Ns3ExponentialVariable_methods(root_module, root_module['ns3::ExponentialVariable']) + register_Ns3GammaVariable_methods(root_module, root_module['ns3::GammaVariable']) register_Ns3IntEmpiricalVariable_methods(root_module, root_module['ns3::IntEmpiricalVariable']) register_Ns3IntegerValue_methods(root_module, root_module['ns3::IntegerValue']) register_Ns3LogNormalVariable_methods(root_module, root_module['ns3::LogNormalVariable']) @@ -1497,6 +1503,25 @@ def register_Ns3EnumValue_methods(root_module, cls): is_virtual=True) return +def register_Ns3ErlangVariable_methods(root_module, cls): + ## random-variable.h: ns3::ErlangVariable::ErlangVariable(ns3::ErlangVariable const & arg0) [copy constructor] + cls.add_constructor([param('ns3::ErlangVariable const &', 'arg0')]) + ## random-variable.h: ns3::ErlangVariable::ErlangVariable() [constructor] + cls.add_constructor([]) + ## random-variable.h: ns3::ErlangVariable::ErlangVariable(unsigned int k, double lambda) [constructor] + cls.add_constructor([param('unsigned int', 'k'), param('double', 'lambda')]) + ## random-variable.h: double ns3::ErlangVariable::GetValue() const [member function] + cls.add_method('GetValue', + 'double', + [], + is_const=True) + ## random-variable.h: double ns3::ErlangVariable::GetValue(unsigned int k, double lambda) const [member function] + cls.add_method('GetValue', + 'double', + [param('unsigned int', 'k'), param('double', 'lambda')], + is_const=True) + return + def register_Ns3ExponentialVariable_methods(root_module, cls): ## random-variable.h: ns3::ExponentialVariable::ExponentialVariable(ns3::ExponentialVariable const & arg0) [copy constructor] cls.add_constructor([param('ns3::ExponentialVariable const &', 'arg0')]) @@ -1508,6 +1533,25 @@ def register_Ns3ExponentialVariable_methods(root_module, cls): cls.add_constructor([param('double', 'm'), param('double', 'b')]) return +def register_Ns3GammaVariable_methods(root_module, cls): + ## random-variable.h: ns3::GammaVariable::GammaVariable(ns3::GammaVariable const & arg0) [copy constructor] + cls.add_constructor([param('ns3::GammaVariable const &', 'arg0')]) + ## random-variable.h: ns3::GammaVariable::GammaVariable() [constructor] + cls.add_constructor([]) + ## random-variable.h: ns3::GammaVariable::GammaVariable(double alpha, double beta) [constructor] + cls.add_constructor([param('double', 'alpha'), param('double', 'beta')]) + ## random-variable.h: double ns3::GammaVariable::GetValue() const [member function] + cls.add_method('GetValue', + 'double', + [], + is_const=True) + ## random-variable.h: double ns3::GammaVariable::GetValue(double alpha, double beta) const [member function] + cls.add_method('GetValue', + 'double', + [param('double', 'alpha'), param('double', 'beta')], + is_const=True) + return + def register_Ns3IntEmpiricalVariable_methods(root_module, cls): ## random-variable.h: ns3::IntEmpiricalVariable::IntEmpiricalVariable(ns3::IntEmpiricalVariable const & arg0) [copy constructor] cls.add_constructor([param('ns3::IntEmpiricalVariable const &', 'arg0')]) diff --git a/bindings/python/ns3_module_csma.py b/bindings/python/ns3_module_csma.py index 17494ae9e..d7e8e9b4f 100644 --- a/bindings/python/ns3_module_csma.py +++ b/bindings/python/ns3_module_csma.py @@ -273,16 +273,6 @@ def register_Ns3CsmaNetDevice_methods(root_module, cls): cls.add_method('GetEncapsulationMode', 'ns3::CsmaNetDevice::EncapsulationMode', []) - ## csma-net-device.h: void ns3::CsmaNetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## csma-net-device.h: std::string ns3::CsmaNetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## csma-net-device.h: void ns3::CsmaNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/ns3_module_emu.py b/bindings/python/ns3_module_emu.py index 54ffede89..4fa5d12a1 100644 --- a/bindings/python/ns3_module_emu.py +++ b/bindings/python/ns3_module_emu.py @@ -80,16 +80,6 @@ def register_Ns3EmuNetDevice_methods(root_module, cls): cls.add_method('SetAddress', 'void', [param('ns3::Mac48Address', 'addr')]) - ## emu-net-device.h: void ns3::EmuNetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## emu-net-device.h: std::string ns3::EmuNetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## emu-net-device.h: void ns3::EmuNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/ns3_module_node.py b/bindings/python/ns3_module_node.py index b214fcda6..d1421012f 100644 --- a/bindings/python/ns3_module_node.py +++ b/bindings/python/ns3_module_node.py @@ -2310,16 +2310,6 @@ def register_Ns3Channel_methods(root_module, cls): is_static=True) ## channel.h: ns3::Channel::Channel() [constructor] cls.add_constructor([]) - ## channel.h: ns3::Channel::Channel(std::string name) [constructor] - cls.add_constructor([param('std::string', 'name')]) - ## channel.h: void ns3::Channel::SetName(std::string arg0) [member function] - cls.add_method('SetName', - 'void', - [param('std::string', 'arg0')]) - ## channel.h: std::string ns3::Channel::GetName() [member function] - cls.add_method('GetName', - 'std::string', - []) ## channel.h: uint32_t ns3::Channel::GetNDevices() const [member function] cls.add_method('GetNDevices', 'uint32_t', @@ -2749,16 +2739,6 @@ def register_Ns3NetDevice_methods(root_module, cls): 'ns3::TypeId', [], is_static=True) - ## net-device.h: void ns3::NetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_pure_virtual=True, is_virtual=True) - ## net-device.h: std::string ns3::NetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_pure_virtual=True, is_const=True, is_virtual=True) ## net-device.h: void ns3::NetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', @@ -3015,16 +2995,6 @@ def register_Ns3SimpleNetDevice_methods(root_module, cls): cls.add_method('SetAddress', 'void', [param('ns3::Mac48Address', 'address')]) - ## simple-net-device.h: void ns3::SimpleNetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## simple-net-device.h: std::string ns3::SimpleNetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## simple-net-device.h: void ns3::SimpleNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/ns3_module_point_to_point.py b/bindings/python/ns3_module_point_to_point.py index 1541d7d25..2360f1f62 100644 --- a/bindings/python/ns3_module_point_to_point.py +++ b/bindings/python/ns3_module_point_to_point.py @@ -175,16 +175,6 @@ def register_Ns3PointToPointNetDevice_methods(root_module, cls): 'uint16_t', [], is_const=True) - ## point-to-point-net-device.h: void ns3::PointToPointNetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## point-to-point-net-device.h: std::string ns3::PointToPointNetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## point-to-point-net-device.h: void ns3::PointToPointNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/ns3_module_tap_bridge.py b/bindings/python/ns3_module_tap_bridge.py index 49b6e651f..060f909a6 100644 --- a/bindings/python/ns3_module_tap_bridge.py +++ b/bindings/python/ns3_module_tap_bridge.py @@ -86,16 +86,6 @@ def register_Ns3TapBridge_methods(root_module, cls): cls.add_method('GetMode', 'ns3::TapBridge::Mode', []) - ## tap-bridge.h: void ns3::TapBridge::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## tap-bridge.h: std::string ns3::TapBridge::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## tap-bridge.h: void ns3::TapBridge::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/ns3_module_wifi.py b/bindings/python/ns3_module_wifi.py index 85394a0a4..f4c2301e5 100644 --- a/bindings/python/ns3_module_wifi.py +++ b/bindings/python/ns3_module_wifi.py @@ -2697,16 +2697,6 @@ def register_Ns3WifiNetDevice_methods(root_module, cls): 'ns3::Ptr< ns3::WifiRemoteStationManager >', [], is_const=True) - ## wifi-net-device.h: void ns3::WifiNetDevice::SetName(std::string const name) [member function] - cls.add_method('SetName', - 'void', - [param('std::string const', 'name')], - is_virtual=True) - ## wifi-net-device.h: std::string ns3::WifiNetDevice::GetName() const [member function] - cls.add_method('GetName', - 'std::string', - [], - is_const=True, is_virtual=True) ## wifi-net-device.h: void ns3::WifiNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', From 8c84b0781e47732913185b61ceeff8ba5986ce4b Mon Sep 17 00:00:00 2001 From: Timo Bingmann Date: Thu, 16 Apr 2009 11:17:31 +0200 Subject: [PATCH 23/49] bug 531: Adds NonUnicastMode attribute to WifiRemoteStationManager --- src/devices/wifi/wifi-remote-station-manager.cc | 14 +++++++++++++- src/devices/wifi/wifi-remote-station-manager.h | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/devices/wifi/wifi-remote-station-manager.cc b/src/devices/wifi/wifi-remote-station-manager.cc index 296a28e7b..b4626b78a 100644 --- a/src/devices/wifi/wifi-remote-station-manager.cc +++ b/src/devices/wifi/wifi-remote-station-manager.cc @@ -97,7 +97,7 @@ NonUnicastWifiRemoteStation::DoReportFinalDataFailed (void) WifiMode NonUnicastWifiRemoteStation::DoGetDataMode (uint32_t size) { - WifiMode mode = m_stations->GetBasicMode (0); + WifiMode mode = m_stations->GetNonUnicastMode (); NS_LOG_DEBUG ("non-unicast size="< Date: Thu, 16 Apr 2009 11:30:25 +0200 Subject: [PATCH 24/49] bug 548: missing license header --- src/devices/wifi/dcf-manager-test.cc | 18 ++++++++++++++++++ src/devices/wifi/dcf-manager.cc | 18 ++++++++++++++++++ src/devices/wifi/interference-helper.cc | 19 +++++++++++++++++++ src/devices/wifi/interference-helper.h | 19 +++++++++++++++++++ src/devices/wifi/wifi-phy-test.cc | 19 +++++++++++++++++++ src/devices/wifi/wifi-test.cc | 19 +++++++++++++++++++ 6 files changed, 112 insertions(+) diff --git a/src/devices/wifi/dcf-manager-test.cc b/src/devices/wifi/dcf-manager-test.cc index 18787ab57..01cf118b2 100644 --- a/src/devices/wifi/dcf-manager-test.cc +++ b/src/devices/wifi/dcf-manager-test.cc @@ -1,4 +1,22 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ #ifdef RUN_SELF_TESTS #include "ns3/test.h" diff --git a/src/devices/wifi/dcf-manager.cc b/src/devices/wifi/dcf-manager.cc index c2dbc9e63..6dd69b31f 100644 --- a/src/devices/wifi/dcf-manager.cc +++ b/src/devices/wifi/dcf-manager.cc @@ -1,4 +1,22 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ #include "ns3/assert.h" #include "ns3/log.h" diff --git a/src/devices/wifi/interference-helper.cc b/src/devices/wifi/interference-helper.cc index 186e6c44d..dff6c5eb9 100644 --- a/src/devices/wifi/interference-helper.cc +++ b/src/devices/wifi/interference-helper.cc @@ -1,3 +1,22 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ #include "interference-helper.h" #include "wifi-phy.h" #include "error-rate-model.h" diff --git a/src/devices/wifi/interference-helper.h b/src/devices/wifi/interference-helper.h index 8e6005031..0b40e239d 100644 --- a/src/devices/wifi/interference-helper.h +++ b/src/devices/wifi/interference-helper.h @@ -1,3 +1,22 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ #ifndef INTERFERENCE_HELPER_H #define INTERFERENCE_HELPER_H diff --git a/src/devices/wifi/wifi-phy-test.cc b/src/devices/wifi/wifi-phy-test.cc index d1ee08880..9f4444416 100644 --- a/src/devices/wifi/wifi-phy-test.cc +++ b/src/devices/wifi/wifi-phy-test.cc @@ -1,3 +1,22 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ #include "wifi-net-device.h" #include "yans-wifi-channel.h" #include "yans-wifi-phy.h" diff --git a/src/devices/wifi/wifi-test.cc b/src/devices/wifi/wifi-test.cc index 045e7081b..2e15b4965 100644 --- a/src/devices/wifi/wifi-test.cc +++ b/src/devices/wifi/wifi-test.cc @@ -1,3 +1,22 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ #ifdef RUN_SELF_TESTS #include "wifi-net-device.h" From fe935b18d2f89c21f8c1779c2ae6b0243890f214 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 16 Apr 2009 12:02:11 +0100 Subject: [PATCH 25/49] waf fix --- wscript | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wscript b/wscript index 85a44430c..ea8d1c49d 100644 --- a/wscript +++ b/wscript @@ -459,6 +459,8 @@ def build(bld): def shutdown(ctx): bld = wutils.bld + if wutils.bld is None: + return env = bld.env #if Options.commands['check']: From 22545849a4286e45196bbefd549b63fb090a1ec6 Mon Sep 17 00:00:00 2001 From: Nicola Baldo Date: Thu, 16 Apr 2009 13:28:51 +0200 Subject: [PATCH 26/49] bug 534: non-const Ptr::operator * () returns const reference --- src/core/ptr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/ptr.h b/src/core/ptr.h index 4861ceb4b..c61db5506 100644 --- a/src/core/ptr.h +++ b/src/core/ptr.h @@ -106,7 +106,7 @@ public: T *operator -> () const; T *operator -> (); const T &operator * () const; - const T &operator * (); + T &operator * (); // allow if (!sp) bool operator! (); // allow if (sp) @@ -472,7 +472,7 @@ Ptr::operator * () const } template -const T & +T & Ptr::operator * () { return *m_ptr; From acb6ddb685923dd0b6b1f53e32f66f45e05f806f Mon Sep 17 00:00:00 2001 From: Timo Bingmann Date: Thu, 16 Apr 2009 15:58:22 +0200 Subject: [PATCH 27/49] make ./waf check pass again. --- src/devices/wifi/wifi-mode.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/devices/wifi/wifi-mode.cc b/src/devices/wifi/wifi-mode.cc index 3fe41c456..132773c28 100644 --- a/src/devices/wifi/wifi-mode.cc +++ b/src/devices/wifi/wifi-mode.cc @@ -87,6 +87,12 @@ WifiMode::GetConstellationSize (void) const std::string WifiMode::GetUniqueName (void) const { + // needed for ostream printing of the invalid mode + if (m_uid == 0) + { + return "Invalid-WifiMode"; + } + struct WifiModeFactory::WifiModeItem *item = WifiModeFactory::GetFactory ()->Get (m_uid); return item->uniqueUid; } From 7ddd83ed2cf7d4a82084d7d2bcabf37e7296f732 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 16 Apr 2009 16:47:02 +0100 Subject: [PATCH 28/49] Detect valgrind memory leak reports; based on patch by Mathieu Lacage; closes bug #532 --- regression.py | 8 ++++---- wscript | 8 ++++---- wutils.py | 42 ++++++++++++++++++++++++++++-------------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/regression.py b/regression.py index d6ae741a6..653731cc5 100644 --- a/regression.py +++ b/regression.py @@ -159,13 +159,13 @@ class regression_test_task(Task.TaskBase): script = os.path.abspath(os.path.join('..', *os.path.split(program))) argv = [self.env['PYTHON'], script] + arguments try: - wutils.run_argv(argv, cwd=trace_output_path) + wutils.run_argv(argv, self.env, cwd=trace_output_path) except Utils.WafError, ex: print >> sys.stderr, ex return 1 else: try: - wutils.run_program(program, + wutils.run_program(program, self.env, command_template=wutils.get_command_template(self.env, arguments), cwd=trace_output_path) except Utils.WafError, ex: @@ -191,13 +191,13 @@ class regression_test_task(Task.TaskBase): script = os.path.abspath(os.path.join('..', *os.path.split(program))) argv = [self.env['PYTHON'], script] + arguments try: - retval = wutils.run_argv(argv, cwd=trace_output_path) + retval = wutils.run_argv(argv, self.env, cwd=trace_output_path) except Utils.WafError, ex: print >> sys.stderr, ex return 1 else: try: - retval = wutils.run_program(program, + retval = wutils.run_program(program, self.env, command_template=wutils.get_command_template(self.env, arguments), cwd=trace_output_path) except Utils.WafError, ex: diff --git a/wscript b/wscript index ea8d1c49d..17b4b38e7 100644 --- a/wscript +++ b/wscript @@ -470,11 +470,11 @@ def shutdown(ctx): lcov_report() if Options.options.run: - wutils.run_program(Options.options.run, wutils.get_command_template(env)) + wutils.run_program(Options.options.run, env, wutils.get_command_template(env)) raise SystemExit(0) if Options.options.pyrun: - wutils.run_python_program(Options.options.pyrun) + wutils.run_python_program(Options.options.pyrun, env) raise SystemExit(0) if Options.options.shell: @@ -516,7 +516,7 @@ def check(bld): if env['ENABLE_PYTHON_BINDINGS']: print "-- Running NS-3 Python bindings unit tests..." - wutils.run_argv([env['PYTHON'], os.path.join("utils", "python-unit-tests.py")], proc_env) + wutils.run_argv([env['PYTHON'], self.env, os.path.join("utils", "python-unit-tests.py")], proc_env) else: print "-- Skipping NS-3 Python bindings unit tests: Python bindings not enabled." @@ -555,7 +555,7 @@ def shell(ctx): shell = os.environ.get("SHELL", "/bin/sh") env = wutils.bld.env - wutils.run_argv([shell], {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])}) + wutils.run_argv([shell], env, {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])}) def doxygen(): if not os.path.exists('doc/introspected-doxygen.h'): diff --git a/wutils.py b/wutils.py index 38a6307b1..d82fe305d 100644 --- a/wutils.py +++ b/wutils.py @@ -11,6 +11,7 @@ import Utils import Logs import TaskGen import Build +import re # these are set from the main wscript file @@ -28,14 +29,7 @@ TRACEBALL_SUFFIX = ".tar.bz2" def get_command_template(env, arguments=()): - if Options.options.valgrind: - if Options.options.command_template: - raise Utils.WafError("Options --command-template and --valgrind are conflicting") - if not env['VALGRIND']: - raise Utils.WafError("valgrind is not installed") - cmd = env['VALGRIND'] + " --leak-check=full --error-exitcode=1 %s" - else: - cmd = Options.options.command_template or '%s' + cmd = Options.options.command_template or '%s' for arg in arguments: cmd = cmd + " " + arg return cmd @@ -120,9 +114,29 @@ def get_proc_env(os_env=None): return proc_env -def run_argv(argv, os_env=None, cwd=None): +def run_argv(argv, env, os_env=None, cwd=None): proc_env = get_proc_env(os_env) - retval = subprocess.Popen(argv, env=proc_env, cwd=cwd).wait() + if Options.options.valgrind: + if Options.options.command_template: + raise Utils.WafError("Options --command-template and --valgrind are conflicting") + if not env['VALGRIND']: + raise Utils.WafError("valgrind is not installed") + argv = [env['VALGRIND'], "--leak-check=full", "--error-exitcode=1"] + argv + proc = subprocess.Popen(argv, env=proc_env, cwd=cwd, stderr=subprocess.PIPE) + reg = re.compile ('definitely lost: ([^ ]+) bytes') + error = False + for line in proc.stderr: + sys.stderr.write(line) + result = reg.search(line) + if result is None: + continue + if result.group(1) != "0": + error = True + retval = proc.wait() + if retval == 0 and error: + retval = 1 + else: + retval = subprocess.Popen(argv, env=proc_env, cwd=cwd).wait() if retval: raise Utils.WafError("Command %s exited with code %i" % (argv, retval)) return retval @@ -169,7 +183,7 @@ def get_run_program(program_string, command_template=None): execvec = shlex.split(command_template % (program_node.abspath(env),)) return program_name, execvec -def run_program(program_string, command_template=None, cwd=None): +def run_program(program_string, env, command_template=None, cwd=None): """ if command_template is not None, then program_string == program name and argv is given by command_template with %s replaced by the @@ -182,17 +196,17 @@ def run_program(program_string, command_template=None, cwd=None): cwd = Options.options.cwd_launch else: cwd = Options.cwd_launch - return run_argv(execvec, cwd=cwd) + return run_argv(execvec, env, cwd=cwd) -def run_python_program(program_string): +def run_python_program(program_string, env): env = bld.env execvec = shlex.split(program_string) if (Options.options.cwd_launch): cwd = Options.options.cwd_launch else: cwd = Options.cwd_launch - return run_argv([env['PYTHON']] + execvec, cwd=cwd) + return run_argv([env['PYTHON']] + execvec, env, cwd=cwd) From 735164d2cdeb0370f256c98917f9e8d82c00baec Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Thu, 16 Apr 2009 22:20:37 +0100 Subject: [PATCH 29/49] script typo fix; bug #551. --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 17b4b38e7..14dc94815 100644 --- a/wscript +++ b/wscript @@ -516,7 +516,7 @@ def check(bld): if env['ENABLE_PYTHON_BINDINGS']: print "-- Running NS-3 Python bindings unit tests..." - wutils.run_argv([env['PYTHON'], self.env, os.path.join("utils", "python-unit-tests.py")], proc_env) + wutils.run_argv([env['PYTHON'], env, os.path.join("utils", "python-unit-tests.py")], proc_env) else: print "-- Skipping NS-3 Python bindings unit tests: Python bindings not enabled." From 5a9a6eab99a9c2832abd52006552f21a0c45fdae Mon Sep 17 00:00:00 2001 From: Federico Maguolo Date: Fri, 17 Apr 2009 09:13:14 +0200 Subject: [PATCH 30/49] bug 536: aarf-cd rate control --- examples/wifi-adhoc.cc | 6 + src/devices/wifi/aarfcd-wifi-manager.cc | 451 ++++++++++++++++++++++++ src/devices/wifi/aarfcd-wifi-manager.h | 133 +++++++ src/devices/wifi/wscript | 1 + 4 files changed, 591 insertions(+) create mode 100644 src/devices/wifi/aarfcd-wifi-manager.cc create mode 100644 src/devices/wifi/aarfcd-wifi-manager.h diff --git a/examples/wifi-adhoc.cc b/examples/wifi-adhoc.cc index 50e99073a..a85d458cc 100644 --- a/examples/wifi-adhoc.cc +++ b/examples/wifi-adhoc.cc @@ -251,6 +251,12 @@ int main (int argc, char *argv[]) dataset = experiment.Run (wifi, wifiPhy, wifiChannel); gnuplot.AddDataset (dataset); + NS_LOG_DEBUG ("aarf-cd"); + experiment = Experiment ("aarf-cd"); + wifi.SetRemoteStationManager ("ns3::AarfcdWifiManager"); + dataset = experiment.Run (wifi, wifiPhy, wifiChannel); + gnuplot.AddDataset (dataset); + NS_LOG_DEBUG ("ideal"); experiment = Experiment ("ideal"); wifi.SetRemoteStationManager ("ns3::IdealWifiManager"); diff --git a/src/devices/wifi/aarfcd-wifi-manager.cc b/src/devices/wifi/aarfcd-wifi-manager.cc new file mode 100644 index 000000000..cfdd08be8 --- /dev/null +++ b/src/devices/wifi/aarfcd-wifi-manager.cc @@ -0,0 +1,451 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2004,2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Federico Maguolo + */ + +#include "aarfcd-wifi-manager.h" +#include "ns3/assert.h" +#include "ns3/log.h" +#include "ns3/simulator.h" +#include "ns3/boolean.h" +#include + +NS_LOG_COMPONENT_DEFINE ("Maarf"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED(AarfcdWifiManager); + +AarfcdWifiRemoteStation::AarfcdWifiRemoteStation (Ptr manager, + int minTimerThreshold, + int minSuccessThreshold, + double successK, + int maxSuccessThreshold, + double timerK, + int minRtsWnd, + int maxRtsWnd, + bool rtsFailsAsDataFails, + bool turnOffRtsAfterRateDecrease, + bool turnOnRtsAfterRateIncrease) + : m_manager (manager) +{ + m_minTimerThreshold = minTimerThreshold; + m_timerTimeout = m_minTimerThreshold; + m_minSuccessThreshold = minSuccessThreshold; + m_successThreshold = m_minSuccessThreshold; + m_successK = successK; + m_maxSuccessThreshold = maxSuccessThreshold; + m_timerK = timerK; + m_rate = GetMinRate (); + + m_success = 0; + m_failed = 0; + m_recovery = false; + m_retry = 0; + m_timer = 0; + m_rtsOn = false; + m_minRtsWnd = minRtsWnd; + m_maxRtsWnd = maxRtsWnd; + m_rtsWnd = m_minRtsWnd; + m_rtsCounter = 0; + m_rtsFailsAsDataFails = rtsFailsAsDataFails; + m_turnOffRtsAfterRateDecrease = turnOffRtsAfterRateDecrease; + m_turnOnRtsAfterRateIncrease = turnOnRtsAfterRateIncrease; + m_justModifyRate = true; + m_haveASuccess = false; +} +AarfcdWifiRemoteStation::~AarfcdWifiRemoteStation () +{} + +uint32_t +AarfcdWifiRemoteStation::GetMaxRate (void) +{ + return GetNSupportedModes () - 1; +} +uint32_t +AarfcdWifiRemoteStation::GetMinRate (void) +{ + return 0; +} + +uint32_t +AarfcdWifiRemoteStation::GetMinTimerTimeout (void) +{ + return m_minTimerThreshold; +} + +uint32_t +AarfcdWifiRemoteStation::GetMinSuccessThreshold (void) +{ + return m_minSuccessThreshold; +} + +void +AarfcdWifiRemoteStation::ReportRecoveryFailure (void) +{ + SetSuccessThreshold ((int)(std::min ((uint32_t)(GetSuccessThreshold () * m_successK), + m_maxSuccessThreshold))); + SetTimerTimeout ((int)(std::max (GetMinTimerTimeout (), + (uint32_t)(GetSuccessThreshold () * m_timerK)))); +} + +void +AarfcdWifiRemoteStation::ReportFailure (void) +{ + SetTimerTimeout (GetMinTimerTimeout ()); + SetSuccessThreshold (GetMinSuccessThreshold ()); +} + +bool +AarfcdWifiRemoteStation::NeedRecoveryFallback (void) +{ + if (m_retry >= 1) + { + return true; + } + else + { + return false; + } +} +bool +AarfcdWifiRemoteStation::NeedNormalFallback (void) +{ + int retryMod = (m_retry - 1) % 2; + if (retryMod == 1) + { + return true; + } + else + { + return false; + } +} + + + +void +AarfcdWifiRemoteStation::DoReportRtsFailed (void) +{ + //printf ("%.9f %p RtsFail %d %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,m_timer,m_retry); + NS_LOG_INFO ("" << this << " RtsFail rate=" << m_rate); + if (m_rtsFailsAsDataFails) { + m_rtsCounter--; + ReportDataFailed (); + } +} +/** + * It is important to realize that "recovery" mode starts after failure of + * the first transmission after a rate increase and ends at the first successful + * transmission. Specifically, recovery mode transcends retransmissions boundaries. + * Fundamentally, ARF handles each data transmission independently, whether it + * is the initial transmission of a packet or the retransmission of a packet. + * The fundamental reason for this is that there is a backoff between each data + * transmission, be it an initial transmission or a retransmission. + */ +void +AarfcdWifiRemoteStation::DoReportDataFailed (void) +{ + NS_LOG_INFO ("" << this << " TxFail rate=" << m_rate); + m_timer++; + m_failed++; + m_retry++; + m_success = 0; + //printf ("%.9f %p Fail %d %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,m_timer,m_retry); + if (!m_rtsOn) { + TurnOnRts (); + if (!m_justModifyRate && !m_haveASuccess) { + //printf ("%p Increase RTS Windows\n",this); + IncreaseRtsWnd (); + } + else { + //printf ("%p Reset RTS Window\n",this); + ResetRtsWnd (); + } + m_rtsCounter = m_rtsWnd; + if (m_retry >= 2) { + m_timer = 0; + } + //printf ("%.9f %p AtcivateRTS %d %d\n",Simulator::Now ().GetSeconds (),this, m_rate, m_rtsCounter); + } + else if (m_recovery) { + NS_ASSERT (m_retry >= 1); + m_justModifyRate = false; + m_rtsCounter = m_rtsWnd; + if (NeedRecoveryFallback ()) { + if (m_turnOffRtsAfterRateDecrease) { + TurnOffRts (); + } + m_justModifyRate = true; + ReportRecoveryFailure (); + if (m_rate != GetMinRate ()) { + m_rate--; + } + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + //printf ("%.9f %p DecreaseRateRecovery %d\n", Simulator::Now ().GetSeconds (),this, m_rate); + } + m_timer = 0; + } + else { + NS_ASSERT (m_retry >= 1); + m_justModifyRate = false; + m_rtsCounter = m_rtsWnd; + if (NeedNormalFallback ()) { + if (m_turnOffRtsAfterRateDecrease) { + TurnOffRts (); + } + m_justModifyRate = true; + ReportFailure (); + if (m_rate != GetMinRate ()) { + m_rate--; + } + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + //printf ("%.9f %p DecreaseRate %d\n", Simulator::Now ().GetSeconds (),this,m_rate); + } + if (m_retry >= 2) { + m_timer = 0; + } + } + CheckRts (); +} +void +AarfcdWifiRemoteStation::DoReportRxOk (double rxSnr, WifiMode txMode) +{} +void +AarfcdWifiRemoteStation::DoReportRtsOk (double ctsSnr, WifiMode ctsMode, double rtsSnr) +{ + NS_LOG_INFO ("" << this << " RtsOk rate=" << m_rate); + NS_LOG_DEBUG ("self="<= GetTimerTimeout ()) { + NS_LOG_INFO ("" << this << " JI rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + } + CheckRts (); +} +void +AarfcdWifiRemoteStation::DoReportFinalRtsFailed (void) +{} +void +AarfcdWifiRemoteStation::DoReportFinalDataFailed (void) +{} + +WifiMode +AarfcdWifiRemoteStation::DoGetDataMode (uint32_t size) +{ + return GetSupportedMode (m_rate); +} +WifiMode +AarfcdWifiRemoteStation::DoGetRtsMode (void) +{ + // XXX: we could/should implement the Arf algorithm for + // RTS only by picking a single rate within the BasicRateSet. + return GetSupportedMode (0); +} + +uint32_t +AarfcdWifiRemoteStation::GetTimerTimeout (void) +{ + return m_timerTimeout; +} +uint32_t +AarfcdWifiRemoteStation::GetSuccessThreshold (void) +{ + return m_successThreshold; +} +void +AarfcdWifiRemoteStation::SetTimerTimeout (uint32_t timerTimeout) +{ + m_timerTimeout = timerTimeout; +} +void +AarfcdWifiRemoteStation::SetSuccessThreshold (uint32_t successThreshold) +{ + m_successThreshold = successThreshold; +} +Ptr +AarfcdWifiRemoteStation::GetManager (void) const +{ + return m_manager; +} + +void +AarfcdWifiRemoteStation::CheckRts (void) +{ + if (m_rtsCounter == 0 && m_rtsOn) { + //printf ("%p Turn off RTS\n",this); + TurnOffRts (); + } +} + +void +AarfcdWifiRemoteStation::TurnOffRts (void) +{ + //printf ("%.9f %p DeatcivateRTS %d %d\n",Simulator::Now ().GetSeconds (),this, m_rate, m_rtsCounter); + m_rtsOn = false; + m_haveASuccess = false; +} + +void +AarfcdWifiRemoteStation::TurnOnRts (void) +{ + m_rtsOn = true; +} + +void +AarfcdWifiRemoteStation::IncreaseRtsWnd (void) +{ + if (m_rtsWnd == m_maxRtsWnd) + return; + + m_rtsWnd *= 2; + if (m_rtsWnd > m_maxRtsWnd) + m_rtsWnd = m_maxRtsWnd; +} + +void +AarfcdWifiRemoteStation::ResetRtsWnd (void) +{ + m_rtsWnd = m_minRtsWnd; +} + +bool +AarfcdWifiRemoteStation::DoNeedRts (Ptr packet) +{ + //printf ("%.9f %p NeedRts %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,(m_rtsOn?1:0)); + NS_LOG_INFO ("" << this << " rate=" << m_rate << " rts=" << (m_rtsOn?"RTS":"BASIC") << " rtsCounter=" << m_rtsCounter); + return m_rtsOn; +} + + + + + +TypeId +AarfcdWifiManager::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::AarfcdWifiManager") + .SetParent () + .AddConstructor () + .AddAttribute ("SuccessK", "Multiplication factor for the success threshold in the AARF algorithm.", + DoubleValue (2.0), + MakeDoubleAccessor (&AarfcdWifiManager::m_successK), + MakeDoubleChecker ()) + .AddAttribute ("TimerK", + "Multiplication factor for the timer threshold in the AARF algorithm.", + DoubleValue (2.0), + MakeDoubleAccessor (&AarfcdWifiManager::m_timerK), + MakeDoubleChecker ()) + .AddAttribute ("MaxSuccessThreshold", + "Maximum value of the success threshold in the AARF algorithm.", + UintegerValue (60), + MakeUintegerAccessor (&AarfcdWifiManager::m_maxSuccessThreshold), + MakeUintegerChecker ()) + .AddAttribute ("MinTimerThreshold", + "The minimum value for the 'timer' threshold in the AARF algorithm.", + UintegerValue (15), + MakeUintegerAccessor (&AarfcdWifiManager::m_minTimerThreshold), + MakeUintegerChecker ()) + .AddAttribute ("MinSuccessThreshold", + "The minimum value for the success threshold in the AARF algorithm.", + UintegerValue (10), + MakeUintegerAccessor (&AarfcdWifiManager::m_minSuccessThreshold), + MakeUintegerChecker ()) + .AddAttribute ("MinRtsWnd", + "Minimum value for Rts window of Aarf-CD", + UintegerValue (1), + MakeUintegerAccessor (&AarfcdWifiManager::m_minRtsWnd), + MakeUintegerChecker ()) + .AddAttribute ("MaxRtsWnd", + "Maximum value for Rts window of Aarf-CD", + UintegerValue (40), + MakeUintegerAccessor (&AarfcdWifiManager::m_maxRtsWnd), + MakeUintegerChecker ()) + .AddAttribute ("RtsFailsAsDataFails", + "If true the RTS failures will be treated by Aarf-CD as Data failures", + BooleanValue (false), + MakeBooleanAccessor (&AarfcdWifiManager::m_rtsFailsAsDataFails), + MakeBooleanChecker ()) + .AddAttribute ("TurnOffRtsAfterRateDecrease", + "If true the RTS mechanism will be turned off when the rate will be decreased", + BooleanValue (true), + MakeBooleanAccessor (&AarfcdWifiManager::m_turnOffRtsAfterRateDecrease), + MakeBooleanChecker ()) + .AddAttribute ("TurnOnRtsAfterRateIncrease", + "If true the RTS mechanism will be turned on when the rate will be increased", + BooleanValue (true), + MakeBooleanAccessor (&AarfcdWifiManager::m_turnOnRtsAfterRateIncrease), + MakeBooleanChecker ()) + ; + return tid; +} +AarfcdWifiManager::AarfcdWifiManager () + : WifiRemoteStationManager () +{} +AarfcdWifiManager::~AarfcdWifiManager () +{} +WifiRemoteStation * +AarfcdWifiManager::CreateStation (void) +{ + return new AarfcdWifiRemoteStation (this, + m_minTimerThreshold, + m_minSuccessThreshold, + m_successK, + m_maxSuccessThreshold, + m_timerK, + m_minRtsWnd, + m_maxRtsWnd, + m_rtsFailsAsDataFails, + m_turnOffRtsAfterRateDecrease, + m_turnOnRtsAfterRateIncrease); +} + +} // namespace ns3 diff --git a/src/devices/wifi/aarfcd-wifi-manager.h b/src/devices/wifi/aarfcd-wifi-manager.h new file mode 100644 index 000000000..da76a6340 --- /dev/null +++ b/src/devices/wifi/aarfcd-wifi-manager.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Federico Maguolo + */ +#ifndef AARFCD_WIFI_MANAGER_H +#define AARFCD_WIFI_MANAGER_H + +#include "wifi-remote-station-manager.h" + +namespace ns3 { + +class AarfcdWifiManager : public WifiRemoteStationManager +{ +public: + static TypeId GetTypeId (void); + AarfcdWifiManager (); + virtual ~AarfcdWifiManager (); + +private: + virtual WifiRemoteStation *CreateStation (void); + uint32_t m_minTimerThreshold; + uint32_t m_minSuccessThreshold; + double m_successK; + uint32_t m_maxSuccessThreshold; + double m_timerK; + uint32_t m_minRtsWnd; + uint32_t m_maxRtsWnd; + bool m_rtsFailsAsDataFails; + bool m_turnOffRtsAfterRateDecrease; + bool m_turnOnRtsAfterRateIncrease; +}; + + +class AarfcdWifiRemoteStation : public WifiRemoteStation +{ +public: + AarfcdWifiRemoteStation (Ptr manager, + int minTimerThreshold, + int minSuccessThreshold, + double successK, + int maxSuccessThreshold, + double timerK, + int minRtsWnd, + int maxRtsWnd, + bool rtsFailsAsDataFails, + bool turnOffRtsAfterRateDecrease, + bool turnOnRtsAfterRateIncrese); + virtual ~AarfcdWifiRemoteStation (); + + +private: + virtual void DoReportRxOk (double rxSnr, WifiMode txMode); + virtual void DoReportRtsFailed (void); + virtual void DoReportDataFailed (void); + virtual void DoReportRtsOk (double ctsSnr, WifiMode ctsMode, double rtsSnr); + virtual void DoReportDataOk (double ackSnr, WifiMode ackMode, double dataSnr); + virtual void DoReportFinalRtsFailed (void); + virtual void DoReportFinalDataFailed (void); + virtual bool DoNeedRts (Ptr packet); + virtual Ptr GetManager (void) const; + virtual WifiMode DoGetDataMode (uint32_t size); + virtual WifiMode DoGetRtsMode (void); + + void ReportRecoveryFailure (void); + void ReportFailure (void); + uint32_t GetMaxRate (void); + uint32_t GetMinRate (void); + void CheckRts (void); + void IncreaseRtsWnd (void); + void ResetRtsWnd (void); + void TurnOffRts (void); + void TurnOnRts (void); + + bool NeedRecoveryFallback (void); + bool NeedNormalFallback (void); + + uint32_t GetTimerTimeout (void); + uint32_t GetSuccessThreshold (void); + uint32_t GetMinSuccessThreshold (void); + uint32_t GetMinTimerTimeout (void); + + void SetTimerTimeout (uint32_t timerTimeout); + void SetSuccessThreshold (uint32_t successThreshold); + + + uint32_t m_timer; + uint32_t m_success; + uint32_t m_failed; + bool m_recovery; + bool m_justModifyRate; + uint32_t m_retry; + + uint32_t m_minTimerThreshold; + uint32_t m_minSuccessThreshold; + double m_successK; + uint32_t m_maxSuccessThreshold; + double m_timerK; + uint32_t m_successThreshold; + uint32_t m_timerTimeout; + + uint32_t m_rate; + bool m_rtsOn; + uint32_t m_rtsWnd; + uint32_t m_minRtsWnd; + uint32_t m_maxRtsWnd; + uint32_t m_rtsCounter; + bool m_haveASuccess; + + Ptr m_manager; + bool m_rtsFailsAsDataFails; + bool m_turnOffRtsAfterRateDecrease; + bool m_turnOnRtsAfterRateIncrease; + +}; + +} // namespace ns3 + +#endif /* MAARF_MAC_STATIONS_H */ diff --git a/src/devices/wifi/wscript b/src/devices/wifi/wscript index fb4016c14..e26ef7a0c 100644 --- a/src/devices/wifi/wscript +++ b/src/devices/wifi/wscript @@ -42,6 +42,7 @@ def build(bld): 'amrr-wifi-manager.cc', 'onoe-wifi-manager.cc', 'rraa-wifi-manager.cc', + 'aarfcd-wifi-manager.cc', 'constant-rate-wifi-manager.cc', 'wifi-test.cc', ] From e8e7ca3ad17022107da3b5cbac15be65f7361576 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 09:17:19 +0200 Subject: [PATCH 31/49] coding style --- src/devices/wifi/aarfcd-wifi-manager.cc | 170 +++++++++++++----------- 1 file changed, 95 insertions(+), 75 deletions(-) diff --git a/src/devices/wifi/aarfcd-wifi-manager.cc b/src/devices/wifi/aarfcd-wifi-manager.cc index cfdd08be8..9ae2460ae 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.cc +++ b/src/devices/wifi/aarfcd-wifi-manager.cc @@ -25,7 +25,7 @@ #include "ns3/boolean.h" #include -NS_LOG_COMPONENT_DEFINE ("Maarf"); +NS_LOG_COMPONENT_DEFINE ("Aarfcd"); namespace ns3 { @@ -167,60 +167,73 @@ AarfcdWifiRemoteStation::DoReportDataFailed (void) m_retry++; m_success = 0; //printf ("%.9f %p Fail %d %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,m_timer,m_retry); - if (!m_rtsOn) { - TurnOnRts (); - if (!m_justModifyRate && !m_haveASuccess) { - //printf ("%p Increase RTS Windows\n",this); - IncreaseRtsWnd (); + if (!m_rtsOn) + { + TurnOnRts (); + if (!m_justModifyRate && !m_haveASuccess) + { + //printf ("%p Increase RTS Windows\n",this); + IncreaseRtsWnd (); + } + else + { + //printf ("%p Reset RTS Window\n",this); + ResetRtsWnd (); + } + m_rtsCounter = m_rtsWnd; + if (m_retry >= 2) + { + m_timer = 0; + } + //printf ("%.9f %p AtcivateRTS %d %d\n",Simulator::Now ().GetSeconds (),this, m_rate, m_rtsCounter); } - else { - //printf ("%p Reset RTS Window\n",this); - ResetRtsWnd (); - } - m_rtsCounter = m_rtsWnd; - if (m_retry >= 2) { + else if (m_recovery) + { + NS_ASSERT (m_retry >= 1); + m_justModifyRate = false; + m_rtsCounter = m_rtsWnd; + if (NeedRecoveryFallback ()) + { + if (m_turnOffRtsAfterRateDecrease) + { + TurnOffRts (); + } + m_justModifyRate = true; + ReportRecoveryFailure (); + if (m_rate != GetMinRate ()) + { + m_rate--; + } + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + //printf ("%.9f %p DecreaseRateRecovery %d\n", Simulator::Now ().GetSeconds (),this, m_rate); + } m_timer = 0; + } + else + { + NS_ASSERT (m_retry >= 1); + m_justModifyRate = false; + m_rtsCounter = m_rtsWnd; + if (NeedNormalFallback ()) + { + if (m_turnOffRtsAfterRateDecrease) + { + TurnOffRts (); + } + m_justModifyRate = true; + ReportFailure (); + if (m_rate != GetMinRate ()) + { + m_rate--; + } + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + //printf ("%.9f %p DecreaseRate %d\n", Simulator::Now ().GetSeconds (),this,m_rate); + } + if (m_retry >= 2) + { + m_timer = 0; + } } - //printf ("%.9f %p AtcivateRTS %d %d\n",Simulator::Now ().GetSeconds (),this, m_rate, m_rtsCounter); - } - else if (m_recovery) { - NS_ASSERT (m_retry >= 1); - m_justModifyRate = false; - m_rtsCounter = m_rtsWnd; - if (NeedRecoveryFallback ()) { - if (m_turnOffRtsAfterRateDecrease) { - TurnOffRts (); - } - m_justModifyRate = true; - ReportRecoveryFailure (); - if (m_rate != GetMinRate ()) { - m_rate--; - } - NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); - //printf ("%.9f %p DecreaseRateRecovery %d\n", Simulator::Now ().GetSeconds (),this, m_rate); - } - m_timer = 0; - } - else { - NS_ASSERT (m_retry >= 1); - m_justModifyRate = false; - m_rtsCounter = m_rtsWnd; - if (NeedNormalFallback ()) { - if (m_turnOffRtsAfterRateDecrease) { - TurnOffRts (); - } - m_justModifyRate = true; - ReportFailure (); - if (m_rate != GetMinRate ()) { - m_rate--; - } - NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); - //printf ("%.9f %p DecreaseRate %d\n", Simulator::Now ().GetSeconds (),this,m_rate); - } - if (m_retry >= 2) { - m_timer = 0; - } - } CheckRts (); } void @@ -250,25 +263,27 @@ AarfcdWifiRemoteStation::DoReportDataOk (double ackSnr, WifiMode ackMode, double if ((m_success == GetSuccessThreshold () || m_timer >= GetTimerTimeout ()) && (m_rate < GetMaxRate ())) - { - NS_LOG_DEBUG ("self="<= GetTimerTimeout ()) { - NS_LOG_INFO ("" << this << " JI rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); - } + m_timer >= GetTimerTimeout ()) + { + NS_LOG_INFO ("" << this << " JI rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + } CheckRts (); } void @@ -320,10 +335,11 @@ AarfcdWifiRemoteStation::GetManager (void) const void AarfcdWifiRemoteStation::CheckRts (void) { - if (m_rtsCounter == 0 && m_rtsOn) { - //printf ("%p Turn off RTS\n",this); - TurnOffRts (); - } + if (m_rtsCounter == 0 && m_rtsOn) + { + //printf ("%p Turn off RTS\n",this); + TurnOffRts (); + } } void @@ -344,11 +360,15 @@ void AarfcdWifiRemoteStation::IncreaseRtsWnd (void) { if (m_rtsWnd == m_maxRtsWnd) - return; + { + return; + } m_rtsWnd *= 2; if (m_rtsWnd > m_maxRtsWnd) - m_rtsWnd = m_maxRtsWnd; + { + m_rtsWnd = m_maxRtsWnd; + } } void From 4ab9fd326388f42033ca3e538d9ba4b76fdb071c Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 09:23:50 +0200 Subject: [PATCH 32/49] use manager variables instead of per-station variables --- src/devices/wifi/aarfcd-wifi-manager.cc | 44 +++++++++---------------- src/devices/wifi/aarfcd-wifi-manager.h | 14 ++------ 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/src/devices/wifi/aarfcd-wifi-manager.cc b/src/devices/wifi/aarfcd-wifi-manager.cc index 9ae2460ae..361b1bbfe 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.cc +++ b/src/devices/wifi/aarfcd-wifi-manager.cc @@ -36,12 +36,7 @@ AarfcdWifiRemoteStation::AarfcdWifiRemoteStation (Ptr manager int minSuccessThreshold, double successK, int maxSuccessThreshold, - double timerK, - int minRtsWnd, - int maxRtsWnd, - bool rtsFailsAsDataFails, - bool turnOffRtsAfterRateDecrease, - bool turnOnRtsAfterRateIncrease) + double timerK) : m_manager (manager) { m_minTimerThreshold = minTimerThreshold; @@ -59,13 +54,8 @@ AarfcdWifiRemoteStation::AarfcdWifiRemoteStation (Ptr manager m_retry = 0; m_timer = 0; m_rtsOn = false; - m_minRtsWnd = minRtsWnd; - m_maxRtsWnd = maxRtsWnd; - m_rtsWnd = m_minRtsWnd; + m_rtsWnd = m_manager->m_minRtsWnd; m_rtsCounter = 0; - m_rtsFailsAsDataFails = rtsFailsAsDataFails; - m_turnOffRtsAfterRateDecrease = turnOffRtsAfterRateDecrease; - m_turnOnRtsAfterRateIncrease = turnOnRtsAfterRateIncrease; m_justModifyRate = true; m_haveASuccess = false; } @@ -144,10 +134,11 @@ AarfcdWifiRemoteStation::DoReportRtsFailed (void) { //printf ("%.9f %p RtsFail %d %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,m_timer,m_retry); NS_LOG_INFO ("" << this << " RtsFail rate=" << m_rate); - if (m_rtsFailsAsDataFails) { - m_rtsCounter--; - ReportDataFailed (); - } + if (m_manager->m_rtsFailsAsDataFails) + { + m_rtsCounter--; + ReportDataFailed (); + } } /** * It is important to realize that "recovery" mode starts after failure of @@ -194,7 +185,7 @@ AarfcdWifiRemoteStation::DoReportDataFailed (void) m_rtsCounter = m_rtsWnd; if (NeedRecoveryFallback ()) { - if (m_turnOffRtsAfterRateDecrease) + if (m_manager->m_turnOffRtsAfterRateDecrease) { TurnOffRts (); } @@ -216,7 +207,7 @@ AarfcdWifiRemoteStation::DoReportDataFailed (void) m_rtsCounter = m_rtsWnd; if (NeedNormalFallback ()) { - if (m_turnOffRtsAfterRateDecrease) + if (m_manager->m_turnOffRtsAfterRateDecrease) { TurnOffRts (); } @@ -271,7 +262,7 @@ AarfcdWifiRemoteStation::DoReportDataOk (double ackSnr, WifiMode ackMode, double m_success = 0; m_recovery = true; m_justModifyRate = true; - if (m_turnOnRtsAfterRateIncrease) + if (m_manager->m_turnOnRtsAfterRateIncrease) { TurnOnRts (); ResetRtsWnd (); @@ -359,22 +350,22 @@ AarfcdWifiRemoteStation::TurnOnRts (void) void AarfcdWifiRemoteStation::IncreaseRtsWnd (void) { - if (m_rtsWnd == m_maxRtsWnd) + if (m_rtsWnd == m_manager->m_maxRtsWnd) { return; } m_rtsWnd *= 2; - if (m_rtsWnd > m_maxRtsWnd) + if (m_rtsWnd > m_manager->m_maxRtsWnd) { - m_rtsWnd = m_maxRtsWnd; + m_rtsWnd = m_manager->m_maxRtsWnd; } } void AarfcdWifiRemoteStation::ResetRtsWnd (void) { - m_rtsWnd = m_minRtsWnd; + m_rtsWnd = m_manager->m_minRtsWnd; } bool @@ -460,12 +451,7 @@ AarfcdWifiManager::CreateStation (void) m_minSuccessThreshold, m_successK, m_maxSuccessThreshold, - m_timerK, - m_minRtsWnd, - m_maxRtsWnd, - m_rtsFailsAsDataFails, - m_turnOffRtsAfterRateDecrease, - m_turnOnRtsAfterRateIncrease); + m_timerK); } } // namespace ns3 diff --git a/src/devices/wifi/aarfcd-wifi-manager.h b/src/devices/wifi/aarfcd-wifi-manager.h index da76a6340..1ccf59662 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.h +++ b/src/devices/wifi/aarfcd-wifi-manager.h @@ -32,6 +32,7 @@ public: virtual ~AarfcdWifiManager (); private: + friend class AarfcdWifiRemoteStation; virtual WifiRemoteStation *CreateStation (void); uint32_t m_minTimerThreshold; uint32_t m_minSuccessThreshold; @@ -54,12 +55,7 @@ public: int minSuccessThreshold, double successK, int maxSuccessThreshold, - double timerK, - int minRtsWnd, - int maxRtsWnd, - bool rtsFailsAsDataFails, - bool turnOffRtsAfterRateDecrease, - bool turnOnRtsAfterRateIncrese); + double timerK); virtual ~AarfcdWifiRemoteStation (); @@ -116,16 +112,10 @@ private: uint32_t m_rate; bool m_rtsOn; uint32_t m_rtsWnd; - uint32_t m_minRtsWnd; - uint32_t m_maxRtsWnd; uint32_t m_rtsCounter; bool m_haveASuccess; Ptr m_manager; - bool m_rtsFailsAsDataFails; - bool m_turnOffRtsAfterRateDecrease; - bool m_turnOnRtsAfterRateIncrease; - }; } // namespace ns3 From 383f8cb0273d40af3fd6b4da523d13d9447b7b16 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 09:34:43 +0200 Subject: [PATCH 33/49] cleanup a bit --- src/devices/wifi/aarfcd-wifi-manager.cc | 83 ++++++------------------- src/devices/wifi/aarfcd-wifi-manager.h | 21 +------ 2 files changed, 19 insertions(+), 85 deletions(-) diff --git a/src/devices/wifi/aarfcd-wifi-manager.cc b/src/devices/wifi/aarfcd-wifi-manager.cc index 361b1bbfe..602583a40 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.cc +++ b/src/devices/wifi/aarfcd-wifi-manager.cc @@ -31,21 +31,11 @@ namespace ns3 { NS_OBJECT_ENSURE_REGISTERED(AarfcdWifiManager); -AarfcdWifiRemoteStation::AarfcdWifiRemoteStation (Ptr manager, - int minTimerThreshold, - int minSuccessThreshold, - double successK, - int maxSuccessThreshold, - double timerK) +AarfcdWifiRemoteStation::AarfcdWifiRemoteStation (Ptr manager) : m_manager (manager) { - m_minTimerThreshold = minTimerThreshold; - m_timerTimeout = m_minTimerThreshold; - m_minSuccessThreshold = minSuccessThreshold; - m_successThreshold = m_minSuccessThreshold; - m_successK = successK; - m_maxSuccessThreshold = maxSuccessThreshold; - m_timerK = timerK; + m_timerTimeout = m_manager->m_minTimerThreshold; + m_successThreshold = m_manager->m_minSuccessThreshold; m_rate = GetMinRate (); m_success = 0; @@ -73,32 +63,20 @@ AarfcdWifiRemoteStation::GetMinRate (void) return 0; } -uint32_t -AarfcdWifiRemoteStation::GetMinTimerTimeout (void) -{ - return m_minTimerThreshold; -} - -uint32_t -AarfcdWifiRemoteStation::GetMinSuccessThreshold (void) -{ - return m_minSuccessThreshold; -} - void AarfcdWifiRemoteStation::ReportRecoveryFailure (void) { - SetSuccessThreshold ((int)(std::min ((uint32_t)(GetSuccessThreshold () * m_successK), - m_maxSuccessThreshold))); - SetTimerTimeout ((int)(std::max (GetMinTimerTimeout (), - (uint32_t)(GetSuccessThreshold () * m_timerK)))); + m_successThreshold = (int)(std::min ((uint32_t)(m_successThreshold * m_manager->m_successK), + m_manager->m_maxSuccessThreshold)); + m_timerTimeout = (int)(std::max (m_manager->m_minTimerThreshold, + (uint32_t)(m_successThreshold * m_manager->m_timerK))); } void AarfcdWifiRemoteStation::ReportFailure (void) { - SetTimerTimeout (GetMinTimerTimeout ()); - SetSuccessThreshold (GetMinSuccessThreshold ()); + m_timerTimeout = m_manager->m_minTimerThreshold; + m_successThreshold = m_manager->m_minSuccessThreshold; } bool @@ -195,7 +173,7 @@ AarfcdWifiRemoteStation::DoReportDataFailed (void) { m_rate--; } - NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << m_successTreshold); //printf ("%.9f %p DecreaseRateRecovery %d\n", Simulator::Now ().GetSeconds (),this, m_rate); } m_timer = 0; @@ -217,7 +195,7 @@ AarfcdWifiRemoteStation::DoReportDataFailed (void) { m_rate--; } - NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << m_successThreshold); //printf ("%.9f %p DecreaseRate %d\n", Simulator::Now ().GetSeconds (),this,m_rate); } if (m_retry >= 2) @@ -251,13 +229,13 @@ AarfcdWifiRemoteStation::DoReportDataOk (double ackSnr, WifiMode ackMode, double //printf ("%.9f %p Ok %d %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,m_timer,m_retry); //printf ("%p OK (m_success=%d, th=%d, m_rate=%d, maxRate=%d)\n",this,m_success,GetSuccessThreshold (), m_rate, GetMaxRate ()); NS_LOG_DEBUG ("self="<= GetTimerTimeout ()) + else if (m_success == m_successThreshold || + m_timer >= m_timerTimeout) { - NS_LOG_INFO ("" << this << " JI rate=" << m_rate << " Sthr=" << GetSuccessThreshold ()); + NS_LOG_INFO ("" << this << " JI rate=" << m_rate << " Sthr=" << m_successThreshold); } CheckRts (); } @@ -297,26 +275,6 @@ AarfcdWifiRemoteStation::DoGetRtsMode (void) return GetSupportedMode (0); } -uint32_t -AarfcdWifiRemoteStation::GetTimerTimeout (void) -{ - return m_timerTimeout; -} -uint32_t -AarfcdWifiRemoteStation::GetSuccessThreshold (void) -{ - return m_successThreshold; -} -void -AarfcdWifiRemoteStation::SetTimerTimeout (uint32_t timerTimeout) -{ - m_timerTimeout = timerTimeout; -} -void -AarfcdWifiRemoteStation::SetSuccessThreshold (uint32_t successThreshold) -{ - m_successThreshold = successThreshold; -} Ptr AarfcdWifiRemoteStation::GetManager (void) const { @@ -446,12 +404,7 @@ AarfcdWifiManager::~AarfcdWifiManager () WifiRemoteStation * AarfcdWifiManager::CreateStation (void) { - return new AarfcdWifiRemoteStation (this, - m_minTimerThreshold, - m_minSuccessThreshold, - m_successK, - m_maxSuccessThreshold, - m_timerK); + return new AarfcdWifiRemoteStation (this); } } // namespace ns3 diff --git a/src/devices/wifi/aarfcd-wifi-manager.h b/src/devices/wifi/aarfcd-wifi-manager.h index 1ccf59662..f4d2d8624 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.h +++ b/src/devices/wifi/aarfcd-wifi-manager.h @@ -50,12 +50,7 @@ private: class AarfcdWifiRemoteStation : public WifiRemoteStation { public: - AarfcdWifiRemoteStation (Ptr manager, - int minTimerThreshold, - int minSuccessThreshold, - double successK, - int maxSuccessThreshold, - double timerK); + AarfcdWifiRemoteStation (Ptr manager); virtual ~AarfcdWifiRemoteStation (); @@ -84,15 +79,6 @@ private: bool NeedRecoveryFallback (void); bool NeedNormalFallback (void); - - uint32_t GetTimerTimeout (void); - uint32_t GetSuccessThreshold (void); - uint32_t GetMinSuccessThreshold (void); - uint32_t GetMinTimerTimeout (void); - - void SetTimerTimeout (uint32_t timerTimeout); - void SetSuccessThreshold (uint32_t successThreshold); - uint32_t m_timer; uint32_t m_success; @@ -101,11 +87,6 @@ private: bool m_justModifyRate; uint32_t m_retry; - uint32_t m_minTimerThreshold; - uint32_t m_minSuccessThreshold; - double m_successK; - uint32_t m_maxSuccessThreshold; - double m_timerK; uint32_t m_successThreshold; uint32_t m_timerTimeout; From 970779ab0e0d8185d9971ee45f12d5792850500b Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 09:56:04 +0200 Subject: [PATCH 34/49] add reference to paper --- src/devices/wifi/aarfcd-wifi-manager.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/devices/wifi/aarfcd-wifi-manager.h b/src/devices/wifi/aarfcd-wifi-manager.h index f4d2d8624..fb287c308 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.h +++ b/src/devices/wifi/aarfcd-wifi-manager.h @@ -24,6 +24,14 @@ namespace ns3 { +/** + * \brief an implementation of the AARF-CD algorithm + * + * This algorithm was first described in "Efficient Collision Detection for Auto Rate Fallback Algorithm". + * The implementation available here was done by Federico Maguolo for a very early development + * version of ns-3. Federico died before merging this work in ns-3 itself so his code was ported + * to ns-3 later without his supervision. + */ class AarfcdWifiManager : public WifiRemoteStationManager { public: From dbf5feba29968fffca480aa4010cb240831813a9 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 09:59:54 +0200 Subject: [PATCH 35/49] cleanup --- src/devices/wifi/aarf-wifi-manager.cc | 31 +++++++++++---------------- src/devices/wifi/aarf-wifi-manager.h | 19 ++++++---------- src/devices/wifi/arf-wifi-manager.cc | 24 +++++++++------------ src/devices/wifi/arf-wifi-manager.h | 10 +++------ 4 files changed, 33 insertions(+), 51 deletions(-) diff --git a/src/devices/wifi/aarf-wifi-manager.cc b/src/devices/wifi/aarf-wifi-manager.cc index 23451d704..765fec03a 100644 --- a/src/devices/wifi/aarf-wifi-manager.cc +++ b/src/devices/wifi/aarf-wifi-manager.cc @@ -71,39 +71,34 @@ AarfWifiManager::~AarfWifiManager () WifiRemoteStation * AarfWifiManager::CreateStation (void) { - return new AarfWifiRemoteStation (this, m_minTimerThreshold, - m_minSuccessThreshold, - m_successK, - m_maxSuccessThreshold, - m_timerK); + return new AarfWifiRemoteStation (this); } -AarfWifiRemoteStation::AarfWifiRemoteStation (Ptr stations, - uint32_t minTimerThreshold, - uint32_t minSuccessThreshold, - double successK, - uint32_t maxSuccessThreshold, - double timerK) - : ArfWifiRemoteStation (stations, minTimerThreshold, minSuccessThreshold), - m_successK (successK), - m_maxSuccessThreshold (maxSuccessThreshold), - m_timerK (timerK) +AarfWifiRemoteStation::AarfWifiRemoteStation (Ptr manager) + : ArfWifiRemoteStation (manager), + m_manager (manager) {} AarfWifiRemoteStation::~AarfWifiRemoteStation () {} +Ptr +AarfWifiRemoteStation::GetManager (void) const +{ + return m_manager; +} + void AarfWifiRemoteStation::ReportRecoveryFailure (void) { - SetSuccessThreshold ((int)(Min (GetSuccessThreshold () * m_successK, - m_maxSuccessThreshold))); + SetSuccessThreshold ((int)(Min (GetSuccessThreshold () * m_manager->m_successK, + m_manager->m_maxSuccessThreshold))); SetTimerTimeout ((int)(Max (GetMinTimerTimeout (), - GetSuccessThreshold () * m_timerK))); + GetSuccessThreshold () * m_manager->m_timerK))); } void diff --git a/src/devices/wifi/aarf-wifi-manager.h b/src/devices/wifi/aarf-wifi-manager.h index 855c327f5..79a70789b 100644 --- a/src/devices/wifi/aarf-wifi-manager.h +++ b/src/devices/wifi/aarf-wifi-manager.h @@ -17,8 +17,8 @@ * * Author: Mathieu Lacage */ -#ifndef AARF_MAC_STATIONS_H -#define AARF_MAC_STATIONS_H +#ifndef AARF_WIFI_MANAGER_H +#define AARF_WIFI_MANAGER_H #include "arf-wifi-manager.h" @@ -39,6 +39,7 @@ public: AarfWifiManager (); virtual ~AarfWifiManager (); private: + friend class AarfWifiRemoteStation; virtual class WifiRemoteStation *CreateStation (void); uint32_t m_minTimerThreshold; uint32_t m_minSuccessThreshold; @@ -50,24 +51,18 @@ private: class AarfWifiRemoteStation : public ArfWifiRemoteStation { public: - AarfWifiRemoteStation (Ptr stations, - uint32_t minTimerThreshold, - uint32_t minSuccessThreshold, - double successK, - uint32_t maxSuccessThreshold, - double timerK); + AarfWifiRemoteStation (Ptr stations); virtual ~AarfWifiRemoteStation (); private: virtual void ReportRecoveryFailure (void); virtual void ReportFailure (void); + virtual Ptr GetManager (void) const; - double m_successK; - uint32_t m_maxSuccessThreshold; - double m_timerK; + Ptr m_manager; }; } // namespace ns3 -#endif /* AARF_MAC_STATIONS_H */ +#endif /* AARF_WIFI_MANAGER_H */ diff --git a/src/devices/wifi/arf-wifi-manager.cc b/src/devices/wifi/arf-wifi-manager.cc index e20e0b73e..fbbc0391f 100644 --- a/src/devices/wifi/arf-wifi-manager.cc +++ b/src/devices/wifi/arf-wifi-manager.cc @@ -28,15 +28,11 @@ NS_LOG_COMPONENT_DEFINE ("ns3::ArfWifiManager"); namespace ns3 { -ArfWifiRemoteStation::ArfWifiRemoteStation (Ptr stations, - int minTimerTimeout, - int minSuccessThreshold) - : m_stations (stations) +ArfWifiRemoteStation::ArfWifiRemoteStation (Ptr manager) + : m_manager (manager) { - m_minTimerTimeout = minTimerTimeout; - m_minSuccessThreshold = minSuccessThreshold; - m_successThreshold = m_minSuccessThreshold; - m_timerTimeout = m_minTimerTimeout; + m_successThreshold = m_manager->m_successThreshold; + m_timerTimeout = m_manager->m_timerThreshold; m_rate = GetMinRate (); m_success = 0; @@ -189,11 +185,11 @@ void ArfWifiRemoteStation::ReportFailure (void) {} uint32_t ArfWifiRemoteStation::GetMinTimerTimeout (void) { - return m_minTimerTimeout; + return m_manager->m_timerThreshold; } uint32_t ArfWifiRemoteStation::GetMinSuccessThreshold (void) { - return m_minSuccessThreshold; + return m_manager->m_successThreshold; } uint32_t ArfWifiRemoteStation::GetTimerTimeout (void) { @@ -205,18 +201,18 @@ uint32_t ArfWifiRemoteStation::GetSuccessThreshold (void) } void ArfWifiRemoteStation::SetTimerTimeout (uint32_t timerTimeout) { - NS_ASSERT (timerTimeout >= m_minTimerTimeout); + NS_ASSERT (timerTimeout >= m_manager->m_timerThreshold); m_timerTimeout = timerTimeout; } void ArfWifiRemoteStation::SetSuccessThreshold (uint32_t successThreshold) { - NS_ASSERT (successThreshold >= m_minSuccessThreshold); + NS_ASSERT (successThreshold >= m_manager->m_successThreshold); m_successThreshold = successThreshold; } Ptr ArfWifiRemoteStation::GetManager (void) const { - return m_stations; + return m_manager; } NS_OBJECT_ENSURE_REGISTERED (ArfWifiManager); @@ -247,7 +243,7 @@ ArfWifiManager::~ArfWifiManager () WifiRemoteStation * ArfWifiManager::CreateStation (void) { - return new ArfWifiRemoteStation (this, m_timerThreshold, m_successThreshold); + return new ArfWifiRemoteStation (this); } } // namespace ns3 diff --git a/src/devices/wifi/arf-wifi-manager.h b/src/devices/wifi/arf-wifi-manager.h index f074bc5d1..6fb68a86b 100644 --- a/src/devices/wifi/arf-wifi-manager.h +++ b/src/devices/wifi/arf-wifi-manager.h @@ -46,6 +46,7 @@ public: virtual ~ArfWifiManager (); private: + friend class ArfWifiRemoteStation; virtual class WifiRemoteStation *CreateStation (void); uint32_t m_timerThreshold; uint32_t m_successThreshold; @@ -55,9 +56,7 @@ private: class ArfWifiRemoteStation : public WifiRemoteStation { public: - ArfWifiRemoteStation (Ptr stations, - int minTimerTimeout, - int minSuccessThreshold); + ArfWifiRemoteStation (Ptr manager); virtual ~ArfWifiRemoteStation (); protected: @@ -85,10 +84,7 @@ private: uint32_t m_rate; - uint32_t m_minTimerTimeout; - uint32_t m_minSuccessThreshold; - - Ptr m_stations; + Ptr m_manager; private: // overriden by AarfMacStation. From e9f4c3f12077b5f7460c9b844af317fb9f42f3bf Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 09:59:59 +0200 Subject: [PATCH 36/49] rescan python --- bindings/python/ns3_module_wifi.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bindings/python/ns3_module_wifi.py b/bindings/python/ns3_module_wifi.py index f4c2301e5..383402cb2 100644 --- a/bindings/python/ns3_module_wifi.py +++ b/bindings/python/ns3_module_wifi.py @@ -727,8 +727,8 @@ def register_Ns3AmrrWifiRemoteStation_methods(root_module, cls): def register_Ns3ArfWifiRemoteStation_methods(root_module, cls): ## arf-wifi-manager.h: ns3::ArfWifiRemoteStation::ArfWifiRemoteStation(ns3::ArfWifiRemoteStation const & arg0) [copy constructor] cls.add_constructor([param('ns3::ArfWifiRemoteStation const &', 'arg0')]) - ## arf-wifi-manager.h: ns3::ArfWifiRemoteStation::ArfWifiRemoteStation(ns3::Ptr stations, int minTimerTimeout, int minSuccessThreshold) [constructor] - cls.add_constructor([param('ns3::Ptr< ns3::ArfWifiManager >', 'stations'), param('int', 'minTimerTimeout'), param('int', 'minSuccessThreshold')]) + ## arf-wifi-manager.h: ns3::ArfWifiRemoteStation::ArfWifiRemoteStation(ns3::Ptr manager) [constructor] + cls.add_constructor([param('ns3::Ptr< ns3::ArfWifiManager >', 'manager')]) ## arf-wifi-manager.h: void ns3::ArfWifiRemoteStation::DoReportRxOk(double rxSnr, ns3::WifiMode txMode) [member function] cls.add_method('DoReportRxOk', 'void', @@ -1667,6 +1667,11 @@ def register_Ns3WifiRemoteStationManager_methods(root_module, cls): 'bool', [], is_const=True) + ## wifi-remote-station-manager.h: ns3::WifiMode ns3::WifiRemoteStationManager::GetNonUnicastMode() const [member function] + cls.add_method('GetNonUnicastMode', + 'ns3::WifiMode', + [], + is_const=True) ## wifi-remote-station-manager.h: ns3::WifiRemoteStation * ns3::WifiRemoteStationManager::Lookup(ns3::Mac48Address address) [member function] cls.add_method('Lookup', 'ns3::WifiRemoteStation *', @@ -1900,8 +1905,8 @@ def register_Ns3YansWifiPhy_methods(root_module, cls): def register_Ns3AarfWifiRemoteStation_methods(root_module, cls): ## aarf-wifi-manager.h: ns3::AarfWifiRemoteStation::AarfWifiRemoteStation(ns3::AarfWifiRemoteStation const & arg0) [copy constructor] cls.add_constructor([param('ns3::AarfWifiRemoteStation const &', 'arg0')]) - ## aarf-wifi-manager.h: ns3::AarfWifiRemoteStation::AarfWifiRemoteStation(ns3::Ptr stations, uint32_t minTimerThreshold, uint32_t minSuccessThreshold, double successK, uint32_t maxSuccessThreshold, double timerK) [constructor] - cls.add_constructor([param('ns3::Ptr< ns3::AarfWifiManager >', 'stations'), param('uint32_t', 'minTimerThreshold'), param('uint32_t', 'minSuccessThreshold'), param('double', 'successK'), param('uint32_t', 'maxSuccessThreshold'), param('double', 'timerK')]) + ## aarf-wifi-manager.h: ns3::AarfWifiRemoteStation::AarfWifiRemoteStation(ns3::Ptr stations) [constructor] + cls.add_constructor([param('ns3::Ptr< ns3::AarfWifiManager >', 'stations')]) ## aarf-wifi-manager.h: void ns3::AarfWifiRemoteStation::ReportRecoveryFailure() [member function] cls.add_method('ReportRecoveryFailure', 'void', @@ -1912,6 +1917,11 @@ def register_Ns3AarfWifiRemoteStation_methods(root_module, cls): 'void', [], visibility='private', is_virtual=True) + ## aarf-wifi-manager.h: ns3::Ptr ns3::AarfWifiRemoteStation::GetManager() const [member function] + cls.add_method('GetManager', + 'ns3::Ptr< ns3::WifiRemoteStationManager >', + [], + is_const=True, visibility='private', is_virtual=True) return def register_Ns3AdhocWifiMac_methods(root_module, cls): From 9e0617b9fc3c6f8698166a6cbb9e8adaa6b44509 Mon Sep 17 00:00:00 2001 From: Federico Maguolo Date: Fri, 17 Apr 2009 10:59:51 +0200 Subject: [PATCH 37/49] cara rate control --- examples/wifi-adhoc.cc | 6 + src/devices/wifi/cara-wifi-manager.cc | 191 ++++++++++++++++++++++++++ src/devices/wifi/cara-wifi-manager.h | 90 ++++++++++++ src/devices/wifi/wscript | 1 + 4 files changed, 288 insertions(+) create mode 100644 src/devices/wifi/cara-wifi-manager.cc create mode 100644 src/devices/wifi/cara-wifi-manager.h diff --git a/examples/wifi-adhoc.cc b/examples/wifi-adhoc.cc index a85d458cc..f9d73f19a 100644 --- a/examples/wifi-adhoc.cc +++ b/examples/wifi-adhoc.cc @@ -257,6 +257,12 @@ int main (int argc, char *argv[]) dataset = experiment.Run (wifi, wifiPhy, wifiChannel); gnuplot.AddDataset (dataset); + NS_LOG_DEBUG ("cara"); + experiment = Experiment ("cara"); + wifi.SetRemoteStationManager ("ns3::CaraWifiManager"); + dataset = experiment.Run (wifi, wifiPhy, wifiChannel); + gnuplot.AddDataset (dataset); + NS_LOG_DEBUG ("ideal"); experiment = Experiment ("ideal"); wifi.SetRemoteStationManager ("ns3::IdealWifiManager"); diff --git a/src/devices/wifi/cara-wifi-manager.cc b/src/devices/wifi/cara-wifi-manager.cc new file mode 100644 index 000000000..d8d73f44c --- /dev/null +++ b/src/devices/wifi/cara-wifi-manager.cc @@ -0,0 +1,191 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2004,2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Federico Maguolo + */ + +#include "cara-wifi-manager.h" +#include "ns3/assert.h" +#include "ns3/log.h" +#include "ns3/simulator.h" + +NS_LOG_COMPONENT_DEFINE ("Cara"); + + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED(CaraWifiManager); + +CaraWifiRemoteStation::CaraWifiRemoteStation (Ptr manager) + : m_manager (manager) +{ + m_rate = GetMinRate (); + + m_success = 0; + m_failed = 0; + m_timer = 0; +} +CaraWifiRemoteStation::~CaraWifiRemoteStation () +{} + +uint32_t +CaraWifiRemoteStation::GetMaxRate (void) +{ + return GetNSupportedModes () - 1; +} +uint32_t +CaraWifiRemoteStation::GetMinRate (void) +{ + return 0; +} + +bool +CaraWifiRemoteStation::NeedNormalFallback (void) +{ + return (m_failed >= m_manager->m_failureThreshold); +} + +void +CaraWifiRemoteStation::DoReportRtsFailed (void) +{} + +void +CaraWifiRemoteStation::DoReportDataFailed (void) +{ + m_timer++; + m_failed++; + m_success = 0; + if (NeedNormalFallback ()) + { + if (m_rate != GetMinRate ()) + { + m_rate--; + } + m_failed = 0; + m_timer = 0; + } +} +void +CaraWifiRemoteStation::DoReportRxOk (double rxSnr, WifiMode txMode) +{} +void +CaraWifiRemoteStation::DoReportRtsOk (double ctsSnr, WifiMode ctsMode, double rtsSnr) +{ + NS_LOG_DEBUG ("self="< +CaraWifiRemoteStation::GetManager (void) const +{ + return m_manager; +} + +bool +CaraWifiRemoteStation::DoNeedRts (Ptr packet) +{ + bool rts = WifiRemoteStation::NeedRts (packet); + if (rts || m_failed >= m_manager->m_probeThreshold) + { + return true; + } + + return false; +} + + + +TypeId +CaraWifiManager::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::CaraWifiManager") + .SetParent () + .AddConstructor () + .AddAttribute ("ProbeThreshold", + "The number of consecutive transmissions failure to activate the RTS probe.", + UintegerValue (1), + MakeUintegerAccessor (&CaraWifiManager::m_probeThreshold), + MakeUintegerChecker ()) + .AddAttribute ("FailureThreshold", + "The number of consecutive transmissions failure to decrease the rate.", + UintegerValue (2), + MakeUintegerAccessor (&CaraWifiManager::m_failureThreshold), + MakeUintegerChecker ()) + .AddAttribute ("SuccessThreshold", + "The minimum number of sucessfull transmissions to try a new rate.", + UintegerValue (10), + MakeUintegerAccessor (&CaraWifiManager::m_failureThreshold), + MakeUintegerChecker ()) + .AddAttribute ("Timeout", + "The 'timer' in the CARA algorithm", + UintegerValue (15), + MakeUintegerAccessor (&CaraWifiManager::m_timerTimeout), + MakeUintegerChecker ()) + ; + return tid; +} + +CaraWifiManager::CaraWifiManager () + : WifiRemoteStationManager () +{} +CaraWifiManager::~CaraWifiManager () +{} + +WifiRemoteStation * +CaraWifiManager::CreateStation (void) +{ + return new CaraWifiRemoteStation (this); +} + +} // namespace ns3 diff --git a/src/devices/wifi/cara-wifi-manager.h b/src/devices/wifi/cara-wifi-manager.h new file mode 100644 index 000000000..4b1e0906b --- /dev/null +++ b/src/devices/wifi/cara-wifi-manager.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Federico Maguolo + */ +#ifndef CARA_WIFI_MANAGER_H +#define CARA_WIFI_MANAGER_H + +#include "wifi-remote-station-manager.h" + +namespace ns3 { + +/** + * \brief implement the CARA rate control algorithm + * + * Implement the CARA algorithm from: + * J. Kim, S. Kim, S. Choi, and D. Qiao. + * "CARA: Collision-Aware Rate Adaptation for IEEE 802.11 WLANs." + * + * Originally implemented by Federico Maguolo for a very early + * prototype version of ns-3. + */ +class CaraWifiManager : public WifiRemoteStationManager +{ +public: + static TypeId GetTypeId (void); + CaraWifiManager (); + virtual ~CaraWifiManager (); + +private: + friend class CaraWifiRemoteStation; + virtual class WifiRemoteStation *CreateStation (void); + uint32_t m_timerTimeout; + uint32_t m_successThreshold; + uint32_t m_failureThreshold; + uint32_t m_probeThreshold; +}; + + +class CaraWifiRemoteStation : public WifiRemoteStation +{ +public: + CaraWifiRemoteStation (Ptr manager); + virtual ~CaraWifiRemoteStation (); + +private: + virtual Ptr GetManager (void) const; + virtual void DoReportRtsFailed (void); + virtual void DoReportRxOk (double rxSnr, WifiMode txMode); + virtual void DoReportDataFailed (void); + virtual void DoReportRtsOk (double ctsSnr, WifiMode ctsMode, double rtsSnr); + virtual void DoReportDataOk (double ackSnr, WifiMode ackMode, double dataSnr); + virtual void DoReportFinalRtsFailed (void); + virtual void DoReportFinalDataFailed (void); + virtual bool DoNeedRts (Ptr packet); + virtual WifiMode DoGetDataMode (uint32_t size); + virtual WifiMode DoGetRtsMode (void); + + uint32_t m_timer; + uint32_t m_success; + uint32_t m_failed; + + uint32_t m_rate; + + Ptr m_manager; + + uint32_t GetMaxRate (void); + uint32_t GetMinRate (void); + + bool NeedNormalFallback (void); + +}; + +} // namespace ns3 + +#endif /* CARA_WIFI_MANAGER_H */ diff --git a/src/devices/wifi/wscript b/src/devices/wifi/wscript index e26ef7a0c..379e46f14 100644 --- a/src/devices/wifi/wscript +++ b/src/devices/wifi/wscript @@ -43,6 +43,7 @@ def build(bld): 'onoe-wifi-manager.cc', 'rraa-wifi-manager.cc', 'aarfcd-wifi-manager.cc', + 'cara-wifi-manager.cc', 'constant-rate-wifi-manager.cc', 'wifi-test.cc', ] From 6148894498e25549b1fbf7b453bfea3ffb6e546a Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 11:06:05 +0200 Subject: [PATCH 38/49] cleanup --- src/devices/wifi/wifi-remote-station-manager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/devices/wifi/wifi-remote-station-manager.h b/src/devices/wifi/wifi-remote-station-manager.h index bd060a183..aead1a308 100644 --- a/src/devices/wifi/wifi-remote-station-manager.h +++ b/src/devices/wifi/wifi-remote-station-manager.h @@ -262,12 +262,9 @@ public: WifiMode GetAckMode (WifiMode dataMode); private: - typedef std::vector SupportedModes; virtual Ptr GetManager (void) const = 0; virtual WifiMode DoGetDataMode (uint32_t size) = 0; virtual WifiMode DoGetRtsMode (void) = 0; - uint32_t GetNFragments (Ptr packet); -protected: virtual void DoReportRtsFailed (void) = 0; virtual void DoReportDataFailed (void) = 0; virtual void DoReportRtsOk (double ctsSnr, WifiMode ctsMode, double rtsSnr) = 0; @@ -275,9 +272,12 @@ protected: virtual void DoReportFinalRtsFailed (void) = 0; virtual void DoReportFinalDataFailed (void) = 0; virtual void DoReportRxOk (double rxSnr, WifiMode txMode) = 0; +protected: uint32_t GetNSupportedModes (void) const; WifiMode GetSupportedMode (uint32_t i) const; private: + typedef std::vector SupportedModes; + uint32_t GetNFragments (Ptr packet); bool IsIn (WifiMode mode) const; WifiMode GetControlAnswerMode (WifiMode reqMode); enum { From b225d123c8499b44063e2ea2fdfe83c81a761257 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 11:06:26 +0200 Subject: [PATCH 39/49] order similarly to base class --- src/devices/wifi/cara-wifi-manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/wifi/cara-wifi-manager.h b/src/devices/wifi/cara-wifi-manager.h index 4b1e0906b..ab3c41fa2 100644 --- a/src/devices/wifi/cara-wifi-manager.h +++ b/src/devices/wifi/cara-wifi-manager.h @@ -60,12 +60,12 @@ public: private: virtual Ptr GetManager (void) const; virtual void DoReportRtsFailed (void); - virtual void DoReportRxOk (double rxSnr, WifiMode txMode); virtual void DoReportDataFailed (void); virtual void DoReportRtsOk (double ctsSnr, WifiMode ctsMode, double rtsSnr); virtual void DoReportDataOk (double ackSnr, WifiMode ackMode, double dataSnr); virtual void DoReportFinalRtsFailed (void); virtual void DoReportFinalDataFailed (void); + virtual void DoReportRxOk (double rxSnr, WifiMode txMode); virtual bool DoNeedRts (Ptr packet); virtual WifiMode DoGetDataMode (uint32_t size); virtual WifiMode DoGetRtsMode (void); From 5846c0fc2a8170ccbe84c46a70b648bc0fdbead9 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 11:43:14 +0200 Subject: [PATCH 40/49] handle rts somewhat better --- src/devices/wifi/aarfcd-wifi-manager.cc | 2 +- src/devices/wifi/aarfcd-wifi-manager.h | 2 +- src/devices/wifi/cara-wifi-manager.cc | 2 +- src/devices/wifi/cara-wifi-manager.h | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/devices/wifi/aarfcd-wifi-manager.cc b/src/devices/wifi/aarfcd-wifi-manager.cc index 602583a40..6048e6224 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.cc +++ b/src/devices/wifi/aarfcd-wifi-manager.cc @@ -327,7 +327,7 @@ AarfcdWifiRemoteStation::ResetRtsWnd (void) } bool -AarfcdWifiRemoteStation::DoNeedRts (Ptr packet) +AarfcdWifiRemoteStation::NeedRts (Ptr packet) { //printf ("%.9f %p NeedRts %d %d\n",Simulator::Now ().GetSeconds (),this,m_rate,(m_rtsOn?1:0)); NS_LOG_INFO ("" << this << " rate=" << m_rate << " rts=" << (m_rtsOn?"RTS":"BASIC") << " rtsCounter=" << m_rtsCounter); diff --git a/src/devices/wifi/aarfcd-wifi-manager.h b/src/devices/wifi/aarfcd-wifi-manager.h index fb287c308..f95d45356 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.h +++ b/src/devices/wifi/aarfcd-wifi-manager.h @@ -70,10 +70,10 @@ private: virtual void DoReportDataOk (double ackSnr, WifiMode ackMode, double dataSnr); virtual void DoReportFinalRtsFailed (void); virtual void DoReportFinalDataFailed (void); - virtual bool DoNeedRts (Ptr packet); virtual Ptr GetManager (void) const; virtual WifiMode DoGetDataMode (uint32_t size); virtual WifiMode DoGetRtsMode (void); + virtual bool NeedRts (Ptr packet); void ReportRecoveryFailure (void); void ReportFailure (void); diff --git a/src/devices/wifi/cara-wifi-manager.cc b/src/devices/wifi/cara-wifi-manager.cc index d8d73f44c..df11efa70 100644 --- a/src/devices/wifi/cara-wifi-manager.cc +++ b/src/devices/wifi/cara-wifi-manager.cc @@ -133,7 +133,7 @@ CaraWifiRemoteStation::GetManager (void) const } bool -CaraWifiRemoteStation::DoNeedRts (Ptr packet) +CaraWifiRemoteStation::NeedRts (Ptr packet) { bool rts = WifiRemoteStation::NeedRts (packet); if (rts || m_failed >= m_manager->m_probeThreshold) diff --git a/src/devices/wifi/cara-wifi-manager.h b/src/devices/wifi/cara-wifi-manager.h index ab3c41fa2..0e47348e6 100644 --- a/src/devices/wifi/cara-wifi-manager.h +++ b/src/devices/wifi/cara-wifi-manager.h @@ -66,10 +66,11 @@ private: virtual void DoReportFinalRtsFailed (void); virtual void DoReportFinalDataFailed (void); virtual void DoReportRxOk (double rxSnr, WifiMode txMode); - virtual bool DoNeedRts (Ptr packet); virtual WifiMode DoGetDataMode (uint32_t size); virtual WifiMode DoGetRtsMode (void); + virtual bool NeedRts (Ptr packet); + uint32_t m_timer; uint32_t m_success; uint32_t m_failed; From f118d2df6bdadfc73096884a3ed742bf2b8258af Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 12:58:22 +0200 Subject: [PATCH 41/49] make debug builds work again --- src/devices/wifi/aarfcd-wifi-manager.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/devices/wifi/aarfcd-wifi-manager.cc b/src/devices/wifi/aarfcd-wifi-manager.cc index 6048e6224..5f1b6c721 100644 --- a/src/devices/wifi/aarfcd-wifi-manager.cc +++ b/src/devices/wifi/aarfcd-wifi-manager.cc @@ -173,7 +173,7 @@ AarfcdWifiRemoteStation::DoReportDataFailed (void) { m_rate--; } - NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << m_successTreshold); + NS_LOG_INFO ("" << this << " JD rate=" << m_rate << " Sthr=" << m_successThreshold); //printf ("%.9f %p DecreaseRateRecovery %d\n", Simulator::Now ().GetSeconds (),this, m_rate); } m_timer = 0; @@ -235,7 +235,7 @@ AarfcdWifiRemoteStation::DoReportDataOk (double ackSnr, WifiMode ackMode, double { NS_LOG_DEBUG ("self="<m_successThreshold || m_timer >= m_manager->m_timerTimeout)) { - NS_LOG_DEBUG ("self="< ()) .AddAttribute ("Timeout", "The 'timer' in the CARA algorithm", From 0ffb6a55495b58f5adfb30951bd425236a41ee91 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 17 Apr 2009 12:44:11 +0100 Subject: [PATCH 43/49] Enable the link option --enable-auto-import on cygwin; closes #525. --- wscript | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wscript b/wscript index 14dc94815..219e70e3d 100644 --- a/wscript +++ b/wscript @@ -246,9 +246,11 @@ def configure(conf): env['PLATFORM'] = sys.platform - if sys.platform == 'win32': - if env['COMPILER_CXX'] == 'g++': + if conf.env['CXX_NAME'] == 'gcc': + if sys.platform == 'win32': env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc") + elif sys.platform == 'cygwin': + env.append_value("LINKFLAGS", "-Wl,--enable-auto-import") conf.sub_config('src') conf.sub_config('utils') From af651b44a9802f1aa7db81f06f4ba67305d3ad06 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 13:45:25 +0200 Subject: [PATCH 44/49] register a correct TypeId --- src/devices/wifi/rraa-wifi-manager.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/devices/wifi/rraa-wifi-manager.cc b/src/devices/wifi/rraa-wifi-manager.cc index 85e9a6c0d..93285a4fa 100644 --- a/src/devices/wifi/rraa-wifi-manager.cc +++ b/src/devices/wifi/rraa-wifi-manager.cc @@ -29,6 +29,8 @@ NS_LOG_COMPONENT_DEFINE ("RraaWifiManager"); namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED(RraaWifiManager); RraaWifiRemoteStation::RraaWifiRemoteStation (Ptr stations) : m_stations (stations) @@ -188,7 +190,7 @@ RraaWifiRemoteStation::ARts (void) TypeId RraaWifiManager::GetTypeId (void) { - static TypeId tid = TypeId ("RraaWifiManager") + static TypeId tid = TypeId ("ns3::RraaWifiManager") .SetParent () .AddConstructor () .AddAttribute ("Basic", From b3507de41fe70930c02ac013480c626ff4497063 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 14:06:20 +0200 Subject: [PATCH 45/49] output rraa simulation --- examples/wifi-adhoc.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/wifi-adhoc.cc b/examples/wifi-adhoc.cc index f9d73f19a..6f0e0bc54 100644 --- a/examples/wifi-adhoc.cc +++ b/examples/wifi-adhoc.cc @@ -263,6 +263,12 @@ int main (int argc, char *argv[]) dataset = experiment.Run (wifi, wifiPhy, wifiChannel); gnuplot.AddDataset (dataset); + NS_LOG_DEBUG ("rraa"); + experiment = Experiment ("rraa"); + wifi.SetRemoteStationManager ("ns3::RraaWifiManager"); + dataset = experiment.Run (wifi, wifiPhy, wifiChannel); + gnuplot.AddDataset (dataset); + NS_LOG_DEBUG ("ideal"); experiment = Experiment ("ideal"); wifi.SetRemoteStationManager ("ns3::IdealWifiManager"); From eb76ef65e953a1db95d3bfc8521865f551e3ab68 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 17 Apr 2009 15:22:41 +0200 Subject: [PATCH 46/49] bug 552: build with gcc 4.4 --- src/core/log.h | 1 + src/devices/emu/emu-encode-decode.cc | 1 + src/devices/tap-bridge/tap-creator.cc | 20 ++++++++++++-------- src/devices/tap-bridge/tap-encode-decode.cc | 1 + 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/core/log.h b/src/core/log.h index 4f62eb47e..ef141f351 100644 --- a/src/core/log.h +++ b/src/core/log.h @@ -23,6 +23,7 @@ #include #include +#include namespace ns3 { diff --git a/src/devices/emu/emu-encode-decode.cc b/src/devices/emu/emu-encode-decode.cc index 49b044099..f61c50d03 100644 --- a/src/devices/emu/emu-encode-decode.cc +++ b/src/devices/emu/emu-encode-decode.cc @@ -20,6 +20,7 @@ #include #include #include +#include namespace ns3 { diff --git a/src/devices/tap-bridge/tap-creator.cc b/src/devices/tap-bridge/tap-creator.cc index a80606e56..fd18752d2 100644 --- a/src/devices/tap-bridge/tap-creator.cc +++ b/src/devices/tap-bridge/tap-creator.cc @@ -138,13 +138,17 @@ AsciiToMac48 (const char *str, uint8_t addr[6]) } } -static void -SetInetAddress (sockaddr *ad, uint32_t networkOrder) +static sockaddr +CreateInetAddress (uint32_t networkOrder) { - struct sockaddr_in *sin = (struct sockaddr_in*)ad; - sin->sin_family = AF_INET; - sin->sin_port = 0; // unused - sin->sin_addr.s_addr = htonl (networkOrder); + union { + struct sockaddr any_socket; + struct sockaddr_in si; + } s; + s.si.sin_family = AF_INET; + s.si.sin_port = 0; // unused + s.si.sin_addr.s_addr = htonl (networkOrder); + return s.any_socket; } static void @@ -330,7 +334,7 @@ CreateTap (const char *dev, const char *gw, const char *ip, const char *mac, con // // Set the IP address of the new interface/device. // - SetInetAddress (&ifr.ifr_addr, AsciiToIpv4 (ip)); + ifr.ifr_addr = CreateInetAddress (AsciiToIpv4 (ip)); status = ioctl (fd, SIOCSIFADDR, &ifr); ABORT_IF (status == -1, "Could not set IP address", true); LOG ("Set device IP address to " << ip); @@ -338,7 +342,7 @@ CreateTap (const char *dev, const char *gw, const char *ip, const char *mac, con // // Set the net mask of the new interface/device // - SetInetAddress (&ifr.ifr_netmask, AsciiToIpv4 (netmask)); + ifr.ifr_netmask = CreateInetAddress (AsciiToIpv4 (netmask)); status = ioctl (fd, SIOCSIFNETMASK, &ifr); ABORT_IF (status == -1, "Could not set net mask", true); LOG ("Set device Net Mask to " << netmask); diff --git a/src/devices/tap-bridge/tap-encode-decode.cc b/src/devices/tap-bridge/tap-encode-decode.cc index 9d85519ab..d6a03588d 100644 --- a/src/devices/tap-bridge/tap-encode-decode.cc +++ b/src/devices/tap-bridge/tap-encode-decode.cc @@ -20,6 +20,7 @@ #include #include #include +#include namespace ns3 { From 27cafe1db267865bb7cd948c14a8bdac706833d5 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 17 Apr 2009 15:11:07 +0100 Subject: [PATCH 47/49] Now _really_ fix #551 --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 219e70e3d..f47c67b67 100644 --- a/wscript +++ b/wscript @@ -518,7 +518,7 @@ def check(bld): if env['ENABLE_PYTHON_BINDINGS']: print "-- Running NS-3 Python bindings unit tests..." - wutils.run_argv([env['PYTHON'], env, os.path.join("utils", "python-unit-tests.py")], proc_env) + wutils.run_argv([env['PYTHON'], os.path.join("utils", "python-unit-tests.py")], env, proc_env) else: print "-- Skipping NS-3 Python bindings unit tests: Python bindings not enabled." From 0c8ec6fa07b7bc8d644ede2655e624ffd09ca1a0 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 17 Apr 2009 15:35:22 +0100 Subject: [PATCH 48/49] HierarchicalMobilityModel: allow the parent model to be NULL, make the child/parent setters public, and add more detailed documentation; closes bug #511 --- src/mobility/hierarchical-mobility-model.cc | 75 +++++++++++++++++---- src/mobility/hierarchical-mobility-model.h | 43 ++++++++++-- 2 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/mobility/hierarchical-mobility-model.cc b/src/mobility/hierarchical-mobility-model.cc index 7a129f052..7896a1f82 100644 --- a/src/mobility/hierarchical-mobility-model.cc +++ b/src/mobility/hierarchical-mobility-model.cc @@ -52,15 +52,46 @@ HierarchicalMobilityModel::HierarchicalMobilityModel () void HierarchicalMobilityModel::SetChild (Ptr model) { + Ptr oldChild = m_child; + Vector pos; + if (m_child) + { + pos = GetPosition (); + m_child->TraceDisconnectWithoutContext ("CourseChange", MakeCallback (&HierarchicalMobilityModel::ChildChanged, this)); + } m_child = model; m_child->TraceConnectWithoutContext ("CourseChange", MakeCallback (&HierarchicalMobilityModel::ChildChanged, this)); + + // if we had a child before, then we had a valid position before; + // try to preserve the old absolute position. + if (oldChild) + { + SetPosition (pos); + } } void HierarchicalMobilityModel::SetParent (Ptr model) { + Vector pos; + if (m_child) + { + pos = GetPosition (); + } + if (m_parent) + { + m_parent->TraceDisconnectWithoutContext ("CourseChange", MakeCallback (&HierarchicalMobilityModel::ParentChanged, this)); + } m_parent = model; - m_parent->TraceConnectWithoutContext ("CourseChange", MakeCallback (&HierarchicalMobilityModel::ParentChanged, this)); + if (m_parent) + { + m_parent->TraceConnectWithoutContext ("CourseChange", MakeCallback (&HierarchicalMobilityModel::ParentChanged, this)); + } + // try to preserve the old position across parent changes + if (m_child) + { + SetPosition (pos); + } } @@ -79,6 +110,10 @@ HierarchicalMobilityModel::GetParent (void) const Vector HierarchicalMobilityModel::DoGetPosition (void) const { + if (!m_parent) + { + return m_child->GetPosition (); + } Vector parentPosition = m_parent->GetPosition (); Vector childPosition = m_child->GetPosition (); return Vector (parentPosition.x + childPosition.x, @@ -88,27 +123,41 @@ HierarchicalMobilityModel::DoGetPosition (void) const void HierarchicalMobilityModel::DoSetPosition (const Vector &position) { - if (m_parent == 0 || m_child == 0) + if (m_child == 0) { return; } // This implementation of DoSetPosition is really an arbitraty choice. // anything else would have been ok. - Vector parentPosition = m_parent->GetPosition (); - Vector childPosition (position.x - parentPosition.x, - position.y - parentPosition.y, - position.z - parentPosition.z); - m_child->SetPosition (childPosition); + if (m_parent) + { + Vector parentPosition = m_parent->GetPosition (); + Vector childPosition (position.x - parentPosition.x, + position.y - parentPosition.y, + position.z - parentPosition.z); + m_child->SetPosition (childPosition); + } + else + { + m_child->SetPosition (position); + } } Vector HierarchicalMobilityModel::DoGetVelocity (void) const { - Vector parentSpeed = m_parent->GetVelocity (); - Vector childSpeed = m_child->GetVelocity (); - Vector speed (parentSpeed.x + childSpeed.x, - parentSpeed.y + childSpeed.y, - parentSpeed.z + childSpeed.z); - return speed; + if (m_parent) + { + Vector parentSpeed = m_parent->GetVelocity (); + Vector childSpeed = m_child->GetVelocity (); + Vector speed (parentSpeed.x + childSpeed.x, + parentSpeed.y + childSpeed.y, + parentSpeed.z + childSpeed.z); + return speed; + } + else + { + return m_child->GetVelocity (); + } } void diff --git a/src/mobility/hierarchical-mobility-model.h b/src/mobility/hierarchical-mobility-model.h index f2d1cda70..5ef664863 100644 --- a/src/mobility/hierarchical-mobility-model.h +++ b/src/mobility/hierarchical-mobility-model.h @@ -27,8 +27,31 @@ namespace ns3 { /** * \brief a hierachical mobility model. * - * This model allows you to specify the position of a - * child object relative to a parent object. + * This model allows you to specify the position of a child object + * relative to a parent object. + * + * Basically this is a mobility model that combines two other mobility + * models: a "parent" model and a "child" model. The position of the + * hierarchical model is always the vector sum of the parent + child + * positions, so that if the parent model "moves", then this model + * will report an equal relative movement. Useful, for instance, if + * you want to simulate a node inside another node that moves, such as + * a vehicle. + * + * Setting the position on this model is always done using world + * absolute coordinates, and it changes only the child mobility model + * position, never the parent. The child mobility model always uses a + * coordinate sytem relative to the parent model position. + * + * @note: as a special case, the parent model may be NULL, which is + * semantically equivalent to having a ConstantPositionMobilityModel + * as parent positioned at origin (0,0,0). In other words, setting + * the parent model to NULL makes the child model and the hierarchical + * model start using world absolute coordinates. + * + * @warning: changing the parent/child mobility models in the middle + * of a simulation will probably not play very well with the + * ConfigStore APIs, so do this only if you know what you are doing. */ class HierarchicalMobilityModel : public MobilityModel { @@ -52,14 +75,26 @@ public: * position by the child mobility model. */ Ptr GetParent (void) const; + /** + * Sets the child mobility model to a new one. If before there + * already existed a child model, then the child mobility model + * current position is also modified to ensure that the composite + * position is preserved. + */ + void SetChild (Ptr model); + /** + * Sets the parent mobility model to a new one. If before there + * already existed a child model, then the child mobility model + * current position is also modified to ensure that the composite + * position is preserved. + */ + void SetParent (Ptr model); private: virtual Vector DoGetPosition (void) const; virtual void DoSetPosition (const Vector &position); virtual Vector DoGetVelocity (void) const; - void SetChild (Ptr model); - void SetParent (Ptr model); void ParentChanged (Ptr model); void ChildChanged (Ptr model); From e22cd479c439bc4060d17111868542e91155954a Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 17 Apr 2009 10:15:11 -0700 Subject: [PATCH 49/49] cleanup raw sockets on Ipv4L3Protocol shutdown --- src/internet-stack/ipv4-l3-protocol.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/internet-stack/ipv4-l3-protocol.cc b/src/internet-stack/ipv4-l3-protocol.cc index 05adfab64..682805728 100644 --- a/src/internet-stack/ipv4-l3-protocol.cc +++ b/src/internet-stack/ipv4-l3-protocol.cc @@ -150,7 +150,13 @@ void Ipv4L3Protocol::DoDispose (void) { NS_LOG_FUNCTION (this); - for (L4List_t::iterator i = m_protocols.begin(); i != m_protocols.end(); ++i) + + for (SocketList::iterator i = m_sockets.begin (); i != m_sockets.end (); ++i) + { + *i = 0; + } + + for (L4List_t::iterator i = m_protocols.begin(); i != m_protocols.end(); ++i) { *i = 0; }