Qualnet完整初始化过程

Qualnet的GUI运行很简单,新建打开已有场景,添加节点,设置各层协议参数,保存运行即可。如果只是用作仿真工具,这样就足够了,但若想自己开发一个协议,或者想深入了解Qualnet底层是如何运行的,那就一定要研究源代码。以下是我在开发过程中,遇到困惑时,耐下心来一步一步学习的结果。在现在看来,这些东西好像很简单,顺理成章,但学习的过程还是很费时费脑的。我是从自己开发用到的几个常用函数,往回倒推,看哪个函数调用它,那个函数还有什么其他作用,找出整个运行过程的。我在此介绍的主要是与我的研究相关的内容,没有将所有的过程都写出来,只是一个比较通用的脉络,供各位参考。

1. 起点

倒推的过程不再絮叨,直接从找到的起点前推叙述。起点位于~/libraries/wireless/src/prop_range.cpp中,里面只有一个main函数。主要代码即功能注释如下。

int main(int argc, char **argv) {
    // 初始化配置文件.
    IO_InitializeNodeInput(&nodeInput, true);

    // 创建空分区.
    partitionData = (PartitionData*)MEM_malloc(sizeof(PartitionData*));
    memset(partitionData, 0, sizeof(PartitionData*));
    partitionData = PARTITION_CreateEmptyPartition(0, 1);
	
    // 读取配置文件.
    IO_ReadNodeInput(&nodeInput, argv[1]);

    // 读取地理信息.
    terrainData.initialize(&nodeInput, true);

    // 读取配置种子,初始化种子信息.
    if (!seedValSet)
    {
        IO_ReadInt(
            ANY_NODEID,
            ANY_ADDRESS,
            &nodeInput,
            "SEED",
            &wasFound,
            &seedVal);
		……
	}

    // 读取配置仿真时间.
    IO_ReadString(
        ANY_NODEID,
        ANY_ADDRESS,
        &nodeInput,
        "SIMULATION-TIME",
        &wasFound,
        buf);

    // 建立节点ID和节点ip地址的映射,为场景节点建立合法ID号.
    MAPPING_BuildAddressMap(
       &nodeInput,
       &numNodes,
       &nodeIdArray,
       addressMapPtr);

    // 分配节点位置数组的内存.
    MOBILITY_AllocateNodePositions(
        numNodes,
        nodeIdArray,
        &nodePositions,
        &nodePlacementTypeCounts,
        &nodeInput,
        seedVal);

    // 读取配置信息,设置节点位置.
    MOBILITY_SetNodePositions(
        numNodes,
        nodePositions,
        nodePlacementTypeCounts,
        &terrainData,
        &nodeInput,
        seed,
        simProps.maxSimClock,
        simProps.startSimClock);
		
    // 利用位置和其他信息,将节点分配至分区.
    int numberOfPartitions = PARALLEL_AssignNodesToPartitions(
                             numNodes,
                             1,
                             &nodeInput,
                             nodePositions,
                             (const AddressMapType*) addressMapPtr);

    // 利用已有的分区内存和各类参数,初始化分区.
    PARTITION_InitializePartition(partitionData,
                                  &terrainData,
                                  simProps.maxSimClock,
                                  startRealTime,
                                  numNodes,
                                  FALSE,
                                  addressMapPtr,
                                  nodePositions,
                                  &nodeInput,
                                  seedVal,
                                  nodePlacementTypeCounts,
                                  experimentPrefix,
                                  simProps.startSimClock);

    // 初始化传播信道.
    PROP_GlobalInit(partitionData, &nodeInput);

    // 利用分区信息,初始化分区内的节点
    PARTITION_InitializeNodes(partitionData);

    // 地理信息确认
    terrainData.finalize();
    return 0;
}

main函数中,通过读取配置文件,初始化了分区和节点。带IO_开头的函数都是读取配置文件的,见fileio.h,只有函数申明,没有找到定义,理解其含义就行。里面的几个重要函数,以及函数中再调用的重要函数,下面一一介绍。

2. 重要函数

2.1 分配内存并初始化节点位置相关信息

函数见~/main/mobility.cpp,主要作用是读取节点配置中的放置方式信息,分配内存并初始化节点位置和移动信息。

void MOBILITY_AllocateNodePositions(
        numNodes,
        nodeIdArray,
        &nodePositions,
        &nodePlacementTypeCounts,
        &nodeInput,
        seedVal);
{
    NodePositions* nodePositions =
        (NodePositions*)MEM_malloc(sizeof(NodePositions) * numNodes);
    int* nodePlacementTypeCounts =
        (int*)MEM_malloc(sizeof(int) * NUM_NODE_PLACEMENT_TYPES);

    for (i = 0; i < NUM_NODE_PLACEMENT_TYPES; i++) {
        nodePlacementTypeCounts[i] = 0;
    }

    for (i = 0; i < numNodes; i++) 
    {
        nodePositions[i].nodeId = nodeIdArray[i];
        nodePositions[i].partitionId = 0;

        //设置节点放置类型        
        IO_ReadString(
            nodeIdArray[i],
            ANY_ADDRESS,
            nodeInput,
            "NODE-PLACEMENT",
            &wasFound,
            buf);        

        if (strcmp(buf, "RANDOM") == 0) {
            nodePositions[i].nodePlacementType = RANDOM_PLACEMENT;
            nodePlacementTypeCounts[RANDOM_PLACEMENT]++;
        }
        else if (strcmp(buf, "UNIFORM") == 0) {
            nodePositions[i].nodePlacementType = UNIFORM_PLACEMENT;
            nodePlacementTypeCounts[UNIFORM_PLACEMENT]++;
        }
        else if (strcmp(buf, "GRID") == 0) {
            nodePositions[i].nodePlacementType = GRID_PLACEMENT;
            nodePlacementTypeCounts[GRID_PLACEMENT]++;
        }
        else if (strcmp(buf, "FILE") == 0) {
            nodePositions[i].nodePlacementType = FILE_BASED_PLACEMENT;
            nodePlacementTypeCounts[FILE_BASED_PLACEMENT]++;
        }
        else if (strcmp(buf, "GROUP") == 0) {
            nodePositions[i].nodePlacementType = GROUP_PLACEMENT;
            nodePlacementTypeCounts[GROUP_PLACEMENT]++;
        }
        else if (strcmp(buf, "EXTERNAL") == 0) {
            nodePositions[i].nodePlacementType = EXTERNAL_PLACEMENT;
            nodePlacementTypeCounts[EXTERNAL_PLACEMENT]++;
        }
        else {
            char errorMessage[MAX_STRING_LENGTH];

            sprintf(errorMessage, "Unknown NODE-PLACEMENT type: %s.\n", buf);
            ERROR_ReportError(errorMessage);
        }

        // 分配内存并初始化节点移动类型        
        nodePositions[i].mobilityData =
            (MobilityData*)MEM_malloc(sizeof(MobilityData));
        memset(nodePositions[i].mobilityData, 0, sizeof(MobilityData));

        MOBILITY_PreInitialize(
            nodeIdArray[i],
            nodePositions[i].mobilityData,
            nodeInput,
            seedVal);
    }		
}

2.1.1 先期初始化节点移动信息

函数见~/main/mobility.cpp,初始化节点移动相关参数。其中移动信息数据结构MobilityData见~/include/mobility.h

void MOBILITY_PreInitialize(
    NodeAddress nodeId,
    MobilityData* mobilityData,
    NodeInput* nodeInput,
    int seedVal)
{    
    // 设置节点移动类型.
    IO_ReadString(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY",
        &wasFound,
        buf);
    if (wasFound) 
    {
        if (strcmp(buf, "NONE") == 0) 
            mobilityData->mobilityType = NO_MOBILITY;

        else if (strcmp(buf, "RANDOM-WAYPOINT") == 0) 
            mobilityData->mobilityType = RANDOM_WAYPOINT_MOBILITY;

        else if (strcmp(buf, "GROUP-MOBILITY") == 0)
            mobilityData->mobilityType = GROUP_MOBILITY;

        else if (strcmp(buf, "TRACE") == 0) 
        {
            ERROR_ReportWarning(
                "\"MOBILITY TRACE\" is obsolete; "
                "use \"MOBILITY FILE\" instead.\n");
            mobilityData->mobilityType = FILE_BASED_MOBILITY;
        }
        else if (strcmp(buf, "FILE") == 0) 
            mobilityData->mobilityType = FILE_BASED_MOBILITY;

        else {
            char errorMessage[MAX_STRING_LENGTH];

            sprintf(errorMessage, "Unknown MOBILITY type: %s.\n", buf);
            ERROR_ReportError(errorMessage);
        }
    }
    else 
    {
        mobilityData->mobilityType = NO_MOBILITY;
    }
    IO_ReadFloat(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-POSITION-GRANULARITY",
        &wasFound,
        &distGran);
    IO_ReadBool(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUND-NODE",
        &wasFound,
        &returnVal);
    IO_ReadBool(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-STATISTICS",
        &wasFound,
        &mobilityStats);
 
    // 为节点下一个位置、当前位置和过去两个点的位置分配内存
    // 实际节点位置在函数MOBILITY_SetNodePositions()中分配
    mobilityData->next =
        (MobilityElement*)MEM_malloc(sizeof(MobilityElement));
    memset(mobilityData->next, 0, sizeof(MobilityElement));

    mobilityData->current =
        (MobilityElement*)MEM_malloc(sizeof(MobilityElement));
    memset(mobilityData->current, 0, sizeof(MobilityElement));
    
    // NUM_PAST_MOBILITY_EVENTS = 2
    for (i = 0; i < NUM_PAST_MOBILITY_EVENTS; i++) {
        mobilityData->past[i] =
            (MobilityElement*)MEM_malloc(sizeof(MobilityElement));
        memset(mobilityData->past[i], 0, sizeof(MobilityElement));
    }
    mobilityData->mobilityVar = NULL;
}

2.2 设置节点位置相关信息

函数见~/libraries/developer/src/mobility_placement.cpp,根据场景配置中的节点放置方式,放置节点(即设置节点位置)

void MOBILITY_SetNodePositions(
    int numNodes,
    NodePositions* nodePositions,
    int* nodePlacementTypeCounts,
    TerrainData* terrainData,
    NodeInput* nodeInput,
    RandomSeed seed,
    clocktype maxSimTime,
    clocktype startSimTime)
{    
    // 随机放置节点
    if (nodePlacementTypeCounts[RANDOM_PLACEMENT] != 0) {
        SetNodePositionsRandomly(
            numNodes,
            nodePlacementTypeCounts[RANDOM_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
    // 统一放置节点
    if (nodePlacementTypeCounts[UNIFORM_PLACEMENT] != 0) {
        SetNodePositionsUniformly(
            numNodes,
            nodePlacementTypeCounts[UNIFORM_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
    // 网格式放置节点
    if (nodePlacementTypeCounts[GRID_PLACEMENT] != 0) {
        SetNodePositionsInGrid(
            numNodes,
            nodePlacementTypeCounts[GRID_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            maxSimTime);
    }
    // 基于文件放置节点
    if (nodePlacementTypeCounts[FILE_BASED_PLACEMENT] != 0) {
        SetNodePositionsWithFileInputs(
            numNodes,
            nodePlacementTypeCounts[FILE_BASED_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            maxSimTime,
            startSimTime);
    }
    // 外部节点
    if (nodePlacementTypeCounts[EXTERNAL_PLACEMENT] != 0) {
        SetNodePositionsExternal(
            numNodes,
            nodePlacementTypeCounts[EXTERNAL_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
    // 按组放置节点
    if (nodePlacementTypeCounts[GROUP_PLACEMENT] != 0) {
        SetNodePositionsInGroup(
            numNodes,
            nodePlacementTypeCounts,
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
}

2.3 利用节点位置和其他信息,将节点分配至分区

函数见~/include/parallel.h,只有函数申明,没有定义。

int PARALLEL_AssignNodesToPartitions(int                   numNodes,
                                     int                   numberOfPartitions,
                                     NodeInput*            nodeInput,
                                     NodePositions*        nodePos,
                                     const AddressMapType* map);

2.4 初始化分区

函数见~/main/partition.cpp。在起点函数中,已经创建了空的分区,上一个函数中已经明确了节点将分配到哪个分区,此函数主要是在前面初始化的基础上,分配分区内节点数据结构的内存。

void PARTITION_InitializePartition(
    PartitionData * partitionData,
    TerrainData*    terrainData,
    clocktype       maxSimClock,
    double          startRealTime,
    int             numNodes,
    BOOL            traceEnabled,
    AddressMapType* addressMapPtr,
    NodePositions*  nodePositions,
    NodeInput*      nodeInput,
    int             seedVal,
    int*            nodePlacementTypeCounts,
    char*           experimentPrefix,
    clocktype       startSimClock)
{
    // 读取配置中是否有设置外部接口
    IO_ReadString(
        partitionData->partitionId,
        ANY_ADDRESS,
        nodeInput,
        "ACTIVATE-EXTERNAL-INTERFACE",
        &wasFound,
        buf);

    // 分配分区内节点数据结构内存
    partitionData->nodeData = (Node**) MEM_malloc(sizeof(Node*) * numNodes);
    memset(partitionData->nodeData, 0, sizeof(Node*) * numNodes);

    // 初始化外部接口列表,需要用到外部接口是注意此处
    EXTERNAL_InitializeInterfaceList(&partitionData->interfaceList,
                                     partitionData);
}

2.5 初始化分区内的节点

函数见~/main/partition.cpp,核心函数,涉及节点协议栈的初始化。

void PARTITION_InitializeNodes(PartitionData* partitionData)
{
    // 创建新节点,并添加入分区
    for (i = 0; i < partitionData->numNodes; i++) {
        Node* node = NODE_CreateNode(partitionData, nodePos[i].nodeId,nodePos[i].partitionId, i);
        partitionData->allNodes->push_back (node);
        node->partitionId = nodePos[i].partitionId;
        if (nodePos[i].partitionId == partitionData->partitionId) {           
            partitionData->nodeData[i] = node;
            MAPPING_HashNodeId(partitionData->nodeIdHash,
                               nodePos[i].nodeId,
                               partitionData->nodeData[i]);
            SCHED_InsertNode(partitionData,
                             partitionData->nodeData[i]);
            AddNodeToList(node, nextNode, partitionData);
            nextNode = node;
        }
    // 自底向上初始化节点协议栈
    // 注意,有的层各节点逐一初始化,有的层统一初始化
    // 逐一初始化追踪协议、移动协议、网络协议先期初始化、物理层协议
    nextNode = partitionData->firstNode;
    while (nextNode != NULL){
        TRACE_Initialize(nextNode, nodeInput);
        MOBILITY_PostInitialize(nextNode, nodeInput);

        if (nextNode->guiOption) {
            GUI_InitNode(nextNode, nodeInput, TIME_getSimTime(nextNode));
        }        
        NETWORK_PreInit(nextNode, nodeInput);
        PHY_Init(nextNode, nodeInput);

    // 统一初始化MAC层协议
    MAC_Initialize(partitionData->firstNode, nodeInput);
    // 逐一初始化本分区节点传输信道
    for (i = 0; i < partitionData->numPartitions; i++) {
        if (partitionData->partitionId == i) {
            nextNode = partitionData->firstNode;
            while (nextNode != NULL){                
                for (j = 0; j < partitionData->numChannels; j++) {
                    nextNode->propChannel = partitionData->propChannel;
                    PROP_Init(nextNode, j, nodeInput);
                }
                nextNode = nextNode->nextNodeData;
            }
        }
    } 
    // 逐一初始化节点网络层、传输层、Socket层、应用层路由协议和用户参与协议
    nextNode = partitionData->firstNode;
    while (nextNode != NULL)
    { 
	if ((nextNode->adaptationData.adaptationProtocol
            == ADAPTATION_PROTOCOL_NONE)
            || (nextNode->adaptationData.endSystem))
        {
            NETWORK_Initialize(nextNode, nodeInput);
            TRANSPORT_Initialize(nextNode, nodeInput);
            SocketLayerInit(nextNode, nodeInput);
            APP_Initialize(nextNode, nodeInput);
            USER_Initialize(nextNode, nodeInput);
        }
        nextNode = nextNode->nextNodeData;
    }
    // 统一初始化应用层流量产生协议、天气变化协议.
    APP_InitializeApplications(partitionData->firstNode, nodeInput);
    WEATHER_Init(partitionData, nodeInput);
    // 输出节点位置信息
    for (i = 0; i < partitionData->numNodes; i++) {
        if (partitionData->nodeData[i] != NULL) {
            NODE_PrintLocation(
                node, PARTITION_GetTerrainPtr(partitionData)->getCoordinateSystem());
		}
	}
}

至此,仅仅是完成了场景初始化阶段,通过读取配置文件,设置了分区和节点的各项协议参数。之后将进入运行阶段,以后再聊。以下为初始化完成是输出信息实例,可以看见将节点分配至分区、读取配置文件、解析应用协议、输出节点位置等过程。


---------------------------- 7月 09, 2020 4:52:50 pm ----------------------------

Launching simulator/emulator: C:/snt/qualnet/6.1/bin/qualnet.exe with args: untitled_1.config -np 1 -interactive 127.0.0.1 5132 -with-snt-gui -friendly qualnet_7月_09_20_16_52_50 
QualNet Developer Version 6.1
Build Number: 201209145
Build Date: Sep 14 2012, 11:58:36
QUALNET_HOME = C:\snt\qualnet\6.1

Attempting license checkout (should take less than 2 seconds) ... success.
No partition assignment given for node 1, assigning to partition 0.
No partition assignment given for node 2, assigning to partition 0.
No partition assignment given for node 3, assigning to partition 0.
No partition assignment given for node 4, assigning to partition 0.
No partition assignment given for node 5, assigning to partition 0.
No partition assignment given for node 6, assigning to partition 0.
No partition assignment given for node 7, assigning to partition 0.
No partition assignment given for node 8, assigning to partition 0.
No partition assignment given for node 9, assigning to partition 0.
No partition assignment given for node 10, assigning to partition 0.
Loading scenario untitled_1.config
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Parsing for application type SUPERVISE
Partition 0, Node 1 (349.38, 1387.45, 0.00).
Partition 0, Node 2 (2112.41, 2209.89, 0.00).
Partition 0, Node 3 (55.29, 69.91, 0.00).
Partition 0, Node 4 (948.83, 841.61, 0.00).
Partition 0, Node 5 (446.85, 763.63, 0.00).
Partition 0, Node 6 (1109.66, 388.37, 0.00).
Partition 0, Node 7 (593.06, 1148.64, 0.00).
Partition 0, Node 8 (227.54, 1002.44, 0.00).
Partition 0, Node 9 (417.61, 437.10, 0.00).
Partition 0, Node 10 (1333.84, 1017.06, 0.00).
Initialization completed in 0.203 sec at 2020-07-09 16:52:51.663

猜你喜欢

转载自blog.csdn.net/zhang1806618/article/details/107232818