本节介绍在DARYN执行过程中不同的功能单元的数据结构及其伪代码。
每个模拟站台的ARMSTRONG节点中都包含着一些独一无二的信息,如节点的唯一标识号(ARMSTRONG操作系统分配给节点的标识符)、与该节点连接的轨道数目、节点间的连接协议以及用于模拟现实计算的延迟时间等。这些信息都存储在数据结构node_info中。每个节点也在通信路径中存储信息,如它们的唯一标识号,即ARMSTRONG操作系统分配给它们的标识符,以及两个链接列表。第一个链接列表包含输入此节点的全部协议;第二个链接列表包含此节点全部输出协议的详细信息。
假设在某次模拟执行过程中,车站计算机控制它西边和南边的所有轨道。可以注意到,由于轨道网络是长方形的,有的车站计算机可能不控制任何轨道。车站计算机控制的每条轨道上的每个节点都有两个列表,第一个列表包含当前对这个轨道的预定请求,这个列表在每个运行周期中都要进行处理,然后被清除;第二个列表存储当前占用该轨道的火车(如果有的话)的唯一出入口。
最后,在通信过程中节点使用了两种结构。第一种结构对应于任意两个节点之间的消息。不论消息的类型如何,ARMSTRONG都要将每条信息扩展到指定的标准长度。由于仿真中要使用的信息类型不止一个,所以数据结构必须是所有这些信息类型的超集。可以想象得到,两个节点之间的消息很可能包含由于类型的差异而造成的一些空白字段。第二个结构对应于每个节点的输出缓冲区。由于使用了无闭塞通信基元,信息传输有时候可能会失败。在这种情况下,必须将信息存储在输出缓冲区中,以便于稍后尝试重新传输。缓冲区保存信息直到传输成功,此时将其复件从输出缓冲区中删除。
该程序也用一个数据结构来存储车载计算机的参数。假定火车具有某些参数,那么当它通过ARMSTRONG的节点时,每个节点就会拥有关于目前停在该节点的火车的各种参数。
ARMSTRONG处理器的外部主机对模型进行初始化,并在每个相应的ARM-STRONG处理器中加载可执行程序的副本。在“main”函数中,节点首先读入由主处理器传送给它的数字标识符。然后,各节点都要执行函数“initial”,从外部输入文件中读取信息。函数“initial”要执行两个动作。第一个动作是与相邻节点建立适当的通信路径。为了达到这个目的,它要使用一个函数调用子程序initialize_proto-cols。该子程序从输入文件中读取所有相邻节点的标识符,然后调出ARMSTRONG操作系统给相邻节点分配的标识符,并进行比较。根据假设,如果一个节点的标识符数值比相邻节点的小,该节点就要发送路径标识符给相邻节点。相反,如果它的标识符数值比相邻节点的大,该节点就要等待接收从相邻节点发来的路径标识符。这个过程为以明确的、一致的模式建立协议提供了保障。第二个动作是,用函数ini-tial读取该节点其他方面的相关信息,如邻近轨道和来自这个节点的火车(如果有的话)。这个过程的伪代码如图4.5所示。
图4.5 主程序和初始化流程
初始化程序完成后,每个节点都启动一个循环执行过程,该过程将在模拟结束时终止。这个循环嵌套在一个while循环中,有五个函数:read_inputs,train_function,track_function,clear_buffers和traveling_trains。接下来详细介绍这些内容:
1)read_inputs函数:这个函数(见图4.6)不断检查所有输入信息的输入协议。它利用一个for循环语句不断扫描输入协议的链表。循环的第一步是通过一个ipc_select命令来检查协议中未确认的数据。如果结果是正确的,就通过ipc_recv命令读取消息。然后,将消息按不同类型进行分类。消息类型分为三种:①另一个节点上的火车发出的轨道请求;②从此节点上某列火车发出的请求被接受或拒绝的信息;③进入该节点的火车信息。根据消息的不同性质,调用不同的子函数执行进一步的处理任务。这些子函数包括:
图4.6 read_inputs流程
①add_request:这个子函数负责增加一个由该节点之外的火车提出的请求,要将该请求添加到适当的轨道预留列表中。它通过一个for循环语句对由节点控制的所有轨道进行扫描,并将新请求增加到预留链表的末尾。
②receive_confirmation:如果从其他节点收到回应,这个子函数就负责更新当前节点上的火车的被确认状态。这里用一个while循环扫描火车的链表,并修改相应的确认域。
③new_train:这个子函数负责将列车的参数增加到当前节点上火车的链表中。首先用一个while循环确定火车链表的尾端,然后把新的火车参数加到尾端上。
当读取并正确处理了所有新信息后,节点需要等待一段延迟时间comm_delay。这个延迟就是前面讨论过的通信延迟。延迟过后,每个节点再次为新信息检查每个输入协议。如前所述,如果结果是正确的,就根据信息类型再进行分类。(www.xing528.com)
2)train_function函数:这个函数(见图4.7)模拟在当前节点的火车行为。利用while循环扫描火车链表,检查每列火车的确认状态。确认状态有三种可能值,对应于不同的值,依次执行不同的子函数。
图4.7 train_function流程
①确认值为-1:这表示火车的最近期的次优选择已经被拒绝了。因此,火车必须重新计算它的第一选择,并且再次提交它。子函数compute_1按照前面叙述的步骤重新计算火车的最优轨道请求。轨道请求重新计算后,调用子函数send_request再次提交请求。显然,如果火车请求使用的是该节点西边或南边的轨道,当前节点就控制这条轨道,所以就在该节点处提交请求。如果火车请求使用的是该节点北边或东边的轨道,该请求就会被转送到控制所请求轨道的节点中。如果send_request已经准备好向另一个节点发送请求,就会调用子函数send_message_to_buffer,将轨道请求发送到接收节点输入缓冲区的链接列表中。
②确认值为1:在这种情况下,火车的最优选择已被拒绝。因此,必须重新计算次优选择并再次提交请求。子函数compute_2计算火车的次优选择,并且通过执行子函数send_request将次优选择中的轨道发送到正确的轨道预留列表中。可以想象,在只能单向移动的地方,火车不可能有次优选择。此时,火车不会计算次优选择的轨道请求,而是继续不断地、定期提交它的最优轨道选择请求。
③确认值是2:这表示火车的轨道请求已经得到确认。执行子函数move和函数send_message_to_buffer,将火车参数传到后续节点的输入缓冲区中。
3)track_function函数:这个函数处理轨道预留列表中的轨道预留信息。首先,确定某火车希望使用的轨道目前是否已被其他火车占用。如果是已被占用,将告知该火车不能使用该轨道,直到占用轨道的火车开走了为止。当轨道可用时,确定预留列表中的所有请求中的最早请求时间,并且批准对应该时间的火车使用该轨道,同时拒绝其他所有请求。于是,轨道预留列表中所有请求所对应的火车都获得了它们的确认状态。这里用一个while循环来扫描火车的链接列表。如果火车在当前节点上,就更新火车的链接列表。如果火车在另一个节点上,就将正确的确认或拒绝信息传送到对应的节点中。鉴于轨道请求信息中是包括返回路径的,利用这些信息,并使用子函数send_message_to_buffer就可以将确认/拒绝消息发回给请求者。
4)clear_buffers函数:这个函数(见图4.8)负责清空输出缓冲区中任何未确认的、未处理的信息。这里使用一个while循环对输出协议链表中的每个输出协议进行扫描。如果输出缓冲区中有一个消息是未被确认的,就利用函数ipc_send发送出去。当消息发送成功,即返回代码值为0时,该消息就会从输出缓冲区中删除掉。如果传送失败,该信息将会被继续保留,以便未来再次传送。
图4.8 clear_buffers流程
5)traveling_trains函数:当火车被允许使用它选择的轨道时,它会沿该轨道驶向后续车站。然而,火车的参数传递到下一个节点时肯定会延迟,而延迟的时间长短与轨道的实际长度与火车的速度之比成正比。这个函数(见图4.9)以下列方式达到预期效果:火车的原始参数,会以消息的形式原封不动地传到下一个节点,但是,直到火车实际在节点的等待时间达到了延迟量乘上一个比例系数后的值时,火车才会计算后续的路径。也就是说,当火车的参数到达后续节点时,该函数首先读取ARMSTRONG节点计时器上的时间值(例如v1)。稍后,当计时器的当前值v2超过v1的延迟量乘上系数后的值时,节点才记录列车的存在信息并允许列车重新计算前进的路线。
图4.9 traveling_trains流程
DARYN是用C语言编写的,长度大约是1500行。这个程序在编译器优化指令“-O4”(4级)下的SUN3/60工作站中进行编译,并且在ARMSTRONG系统中运行。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。