当前位置:首页 > Flexsim中的重要概念及开发技术
insertdeallocatetask(myts, opkey); //发布协同任务序列
dispatchcoordinatedtasksequence(myts);
5.2.4.3 对象Dispatcher及任务序列的分配规则
现在考虑一种较为复杂的情况:有两个Queue对象用于存放物件,三个Operator对象用于搬运物件;三个Operator是自由的,没有被分配给固定的Queue,那么怎样来有效地调用这三个Operator呢?此时就要用到Dispatcher对象。
Dispatcher用来控制一组Transporter或Operator。任务序列从一个静态对象发送到Dispatcher,然后Dispatcher来调配这些任务序列分配给与其输出端口相连的动态对象。动态对象接收到任务序列后执行相应的命令序列。
Dispatcher对象的功能就是将任务序列进行队列存储和发送任务序列。根据用户建模的逻辑,任务序列可以被排队等待或是立即传送个相应的对象。Dispatcher的参数设置对话框只有两项,当接收到一个任务序列时,调用“Pass To”函数。顾名思义,该函数将任务序列发送给接收对象;如果该函数返回值是0,即该任务序列不能被立即分配,则根据“QueueStrategy”定义的规则将任务序列放入队列中等候。QueueStrategy函数返回任务序列的相关值,然后根据优先级来确定任务序列在队列中的位置。高优先级的任务序列放在队列的前面,低优先级的放在队列的后面。如果优先级相同,则根据队列的先进先出(FIFO)原则来处理。用户可以根据需要,动态的改变任务序列的优先级。
当将队列中的任务序列进行排序时,Dispatcher执行队列策略函数,遍历取得已有任务序列的优先级值,与最新的任务序列优先级值比较,根据比较的结果重新进行队列排序。
在Flexsim对象层次图中,我们发现Dispatcher是所有TaskExecuter的父类,也就是说所有的TaskExecuter也是Dispatcher。这就意味着Operator或Transporter也可以担当Dispatcher的角色来分配任务序列,或者是自己执行任务序列。
5.2.4.4 Dispatcher与TaskExecuter的区别
在仿真执行的任意时刻,即使任务序列的等候队列中多个任务序列,TaskExecuter一次只能执行一个任务序列。而Dispatcher对象的作用只是在缓存队列中存放已排序好任务序列,并将队列最前面的任务序列发送给动态对象,但并不执行任务序列。
5.2.4.5 利用任务序列实现集装箱的装卸过程
在集装箱码头的作业的过程中,集卡行驶到岸桥设备处等待装箱,岸桥将集装箱从船上卸下装到已等待的集卡上;装箱后的集卡行驶到堆场中,场桥将集装箱从集卡上卸下,堆放到堆场中。集装箱从船到堆场的过程中,经过了集卡、岸桥、场桥等设备的搬运,在Flexsim中就需要使用任务序列来完成这个过程。
这里涉及了三个可运动对象:集卡、岸桥和场桥。这里设计的思路是这样的,集装箱的运输由集卡来实现,这样集卡就有这样一个任务序列:Travel ? Load ? Travel ? Unload。集装箱装入集卡的作业由岸桥设备完成,卸载放入堆场的作业由场桥设备完成,所以集卡的任务序列中Load/Unload的任务就应该由岸桥和场桥来完成。
岸桥完成一次作业的过程也就是完成一个任务序列的过程,可以知道岸桥完成的任务序列应该是:Travel ? Load ? Travel ? Unload。岸桥在作业的过程中,集卡处于等待的状态,也就是说岸桥和集卡之间是协同作业的。场桥的情况与岸桥一致。Flexsim中可以使用调用子任务的方法将岸桥和场桥的任务序列插入到集卡的任务序列中。图1-7表示了主任务序列和子任务序列之间的关系。
集卡主任务序列 Travel Load Travel Unload 岸桥子任务序列 Travel Load Travel Unload 场桥子任务序列 Travel Load Travel Unload
图1-7 集装箱搬运过程的任务序列
在Flexsim中的实现的主要代码如下,其关键的代码在文中有注释: //获取任务序列中的任务数量
int nroftasks = gettotalnroftasks(tasksequence);
//查找Load/Unload任务,找到之后调用子任务来替换这两个任务
for(int i=1; i<=nroftasks; i++) { int tasktype = gettasktype(tasksequence, i); switch(tasktype) { case TASKTYPE_LOAD: case TASKTYPE_FRLOAD: { int msgtype = (tasktype == TASKTYPE_LOAD ? 1 : 2); //changetask(…)函数会发出一个消息(message),我们在消息的接受者的OnMessage(…)函 //数中创建岸桥和叉车的子任务序列
changetask(tasksequence, i, TASKTYPE_CALLSUBTASKS, current, NULL, msgtype, tonum(gettaskinvolved(tasksequence, i, 1)), tonum(gettaskinvolved(tasksequence, i, 2)), gettaskvariable(tasksequence, i, 1));
}
break; case TASKTYPE_UNLOAD: case TASKTYPE_FRUNLOAD: { int msgtype = (tasktype == TASKTYPE_UNLOAD ? 3: 4); changetask(tasksequence, i, TASKTYPE_CALLSUBTASKS, current, NULL, msgtype, tonum(gettaskinvolved(tasksequence, i, 1)), tonum(gettaskinvolved(tasksequence, i, 2)), gettaskvariable(tasksequence, i, 1));
} break; default: break; } }
子任务序列的实现过程,关键代码有注释
//由changetask()传过来的参数msgtype
int msgtype = msgparam(1); switch(msgtype)
{//这里只实现了岸桥子任务序列 case 1: case 2: { fsnode* op = msgsendingobject; fsnode* item = tonode(msgparam(2)); fsnode* queue = up(item);//queue1 fsnode* active_ts = gettasksequence(op, 0); //0表示当前活动的任务序列 int port = gettaskvariable(active_ts, getcurtask(active_ts), 4); fsnode* sts = centerobject(queue, 2); //注意连接时候的端口号 fsnode* ts = createcoordinatedtasksequence(op);//创建协同工作序列 int op_key = insertallocatetask(ts, op, 0, 0); int sync_key1 = insertproxytask(ts, op_key, TASKTYPE_PICKOFFSET, item, queue, 0, 1, 0);
int sts_key = insertallocatetask(ts, sts, tonum(queue), 0); insertproxytask(ts, sts_key, TASKTYPE_TRAVEL, queue, NULL, 0); int sync_key2 = insertproxytask(ts, sts_key, msgtype == 1 ? TASKTYPE_FRLOAD : TASKTYPE_LOAD, item, queue, port);
insertsynctask(ts, sync_key1); insertsynctask(ts, sync_key2); int sync_key3 = insertproxytask(ts, sts_key, TASKTYPE_UNLOAD, item, op, port);
insertsynctask(ts, sync_key3); insertdeallocatetask(ts, op_key); insertdeallocatetask(ts, sts_key); return tonum(ts); } break; … }
具体的代码实现部分有些复杂,但是我们通过前面的分析,将思路整理清楚了,实现也
就相对容易了。
5.3 运动学(Kinematics)
运动学部分是Flexsim3.06版中新引入的功能。Flexsim软件的一大特点就是三维显示功能非常强大,除了能够处理数据统计外,还能使模型的场景中的可运动设备动起来,从而使模拟过程更接近真实。例如,对于港口设备岸边集装箱岸桥,在系统建模中如果我们只关心数据、处理时间等的话,可以用Processor来简单代替岸桥的功能。在Flexsim中可以将岸桥完全实现,不仅在外观上,更重要的是设备处理物件的动作。要实现设备的动作,平移(水平运动,或垂直运动),或是旋转运动,这些都需要用到运动学的知识。
本节主要介绍运动学相关知识,例如坐标空间转换,运动实现,模型的导入,尺寸大小的设定等,还将详细讲述如何在Flexsim中创建用户自己的专门对象库。
5.3.1 FLexsim中的坐标空间
运动功能允许一个对象同时实现多个移动操作,在每个运动方向都有加速度、减速度、起始速度、结束速度以及最大速度等属性。例如集装箱岸桥的运动就是由多个部件的运动组成,即大车沿着轨道运行,小车在大车上运行,吊具在垂直方向上运行。大车、小车和吊具都有其自己的速度属性。如果有了岸桥这样一个设备对象,在仿真过程中我们就可以实现岸桥的作业过程。运动学函数的引入就是帮助用户来实现自己定制的设备的动作。运动学这部分是从3.06版才开始引入的,新版本还会对这部分进一步改进。
实现运动学并不难,主要是对三个函数的使用。要执行运动操作,首先要调用initkinematics命令。该命令为运动初始化数据,保存对象的起始位置、起始角度。初始化完毕之后,调用addkinematic命令为对象添加平移或旋转动作。例如,用户告诉对象在5秒钟时开始运动,给定加速度、减速度和最大速度,在x方向上平移10个单位;然后告诉对象在7秒钟时,用不同的加速度、减速度和最大速度,在y方向上移动10个单位。这两个运动的结果是:对象先在x方向上运动,然后同时在y方向上加速,最后到达目的点的运动轨迹是抛物线。每一个单独的运动通过addkinematic命令添加;然后调用updatekinematics命令在运动过程中不断地刷新视图,该命令的作用就是计算对象当前的位置和旋转角度。
上面的例子很简单,为了更好的解释运动学,我们先介绍坐标空间的概念。Flexsim中最常用的坐标空间就是模型空间(model)。用户建立系统模型时,将许多对象放入视图中,根据不同的逻辑关系组成不同的模型。这些对象都处于模型空间中。模型空间是Flexsim中最大的坐标空间,系统模型中的所有对象都被包含在这个空间当中。
还有一种容器坐标空间(container)。容器对象就是可以存储物件,举例来说,对象Queue的作用是暂存物件,此时Queue就相当于一个容器。当物件置于Queue中时,物件(item)就处于Queue的容器空间中。当一个物体处于不同的容器空间中时,他的位置坐标就是他所在容器坐标空间坐标系的值。图1-5描述了容器空间的概念。
共分享92篇相关文档