flexsim建立socket通信

1,993 阅读15分钟

本篇为flexsim基础操作及socket通信搭建。本文纯手打,图片均为实操截图,转载须注明出处。

本文将介绍flexsim的基础操作、简单函数以及socket通信。本文不涉及的部分,读者可以有针对性地查询用户手册。

相关链接

flexsim基础

  • 软件就长这样

image.png

左边显示的新建打开应该都懂吧。现在新建一个模型,默认参数直接ok进去就行,预期界面如下。

image.png

资源栏

library是资源库,toolbox是工具箱。

image.pngimage.png

如果你不小心关掉了这两栏,toolbox可以从选项栏view里打开,也可以直接点选项栏下边的快捷栏里的tools打开。libraryview选项里open default workspace打开。

参数栏

在右边会显示你选中资源的参数信息。如果你双击某个资源,能看到更详细的界面。右边的简略参数信息,只是为了让你看看当前资源的状态,做一些摆放调整操作,这样会更方便。

image.png

快捷栏

  • 这些栏目的命名都是我自己随便打的,实际怎么分需要去看官方的文档,我只是做一个全面简洁的介绍。 image.png
  • 第一行:
    • 新建、打开、保存
    • 标准指针,建立连接,取消连接(后两者基本不用,有快捷键)
    • 其他(自己点开看看就知道了)
  • 第二行:
    • reset:重置整个模型
    • run:开始
    • stop:停止
    • step:跨到下一步(一般也不用,调时间就行了,除非你要很细致)
    • run time:显示运行的时间(不是真实时间,是模型运行的‘时间’)
    • run speed:设置模型运行的速度,自行调整

连接操作

  • A连接:
    • A连接是对应输入输出关系的连接,可用快捷栏里的建立连接,选择对应的A连接即可使用。除此之外,还有快捷键A,按住A键,依次点击两个实例资源,即刻达成前者向后者输出的连接
    • 取消A连接,可以使用快捷栏里的取消连接,也可以按住快捷键Q,依次点击两个实例,就能够取消A连接。
  • S连接:
    • S连接是实例与执行器之间的连接。若要达成s从a运输box达到b(或是a使用s运输box到b)的效果,可以使用S连接,从a连接到s。
    • 与A连接相同,取消S连接,按住快捷键W即可。
  • D连接:
    • 跟网络节点有关。
    • 取消D连接,按住E快捷键即可。

下面是实操展示。

拉出一个source实例和一个queue实例,使用a连接从source连接至a,点击运行run。需要等待一段时间,queue才会出现box。这是因为source的初始设置里,到达时间是exponential函数,越来越快,但在run speed初始设置为4的时候,就有点慢了,可以勾选arrival at time 0,也可以调整速度,还可以点击step。

尝试各种操作,你可以更快地熟悉flexsim。

最后的结果,就如下图所示。当这条输出成功导通后,你可以发现连接在queue上的三角标已经变成了绿色。

image.png

image.png

之后,我们从task executers中拉出一个operator,使用s连接从source连到operator,他将成为我们的苦力。点击运行run,等待片刻,你会发现他并不干活。其原因是source并未选择调用执行器。我们需要选中source,在右侧属性的output栏目,亦或是双击source,在属性窗口中点击flow标签找到output栏目。勾选use transport即可。

image.png

reset之后run,就能看到operator从source搬运box至queue,并且一直重复该项工作。

image.png

一个基本的模型就已经实现了。

library

library里的所有实例资源,都是可以拖拽到模型里的。初学者建议实操看看样式。

下面将对部分常用资源进行说明,未提及的资源请查操作手册或者百度。

fixed resources

  • source:item流入的源头,你可以设置流入的东西是什么。

    • 如图所示,source的属性有五个标签页,下面介绍常用的四个标签页及其常用功能。 image.png
    • source
      • arrival style:到达方式(一般为到达时间间隔)
      • flowitem class:流中的物件类型,也就是从source流出来的item类型,默认是box,也就是盒子。
      • arrival at time 0:0时间到达,也就是你run起来,就会有一个item出来,不需要去等。
      • inter-arrivaltime:到达时间间隔,默认是指数函数。下拉菜单也提供了很多其他函数的选项,但不要拘泥于函数,直接往里写个常数或是变量,也许更适合实际需求。这与后续的代码部分有一定关联。
      • 函数部分不多赘述,自行探索。
    • flow
      • ouput:指明source流向。
      • send to port:默认为first available,即第一个可用。也就是你先连的哪个,就用哪个。在下拉菜单中,也可以设置多种传输规则,比如按照item的属性、随机、百分比等。
      • use transport:使用运输工具。
    • triggers
      • 该页面默认为空,需要自行增设触发器。该功能非常重要,后面单列讲解。
    • general
      • 主要关注ports中的input和output,其会展示出入端口。其他内容可在右侧属性栏进行调整,无需专门在此调整。
  • queue:队列,所有的item到这儿都会暂存起来。

    image.png

    • queue
      • maximum content:最大库存
      • LIFO:last in first out 后进先出
      • batching:批量操作
      • visual:视觉效果
    • flow
      • output同source
      • input类似ouput,可自行摸索。
  • processor:处理器,可以对item进行各种处理。

    image.png

    • processor
      • setup time:安装时间
      • process time:处理时间
  • sink:流出/回收。

    image.png

    • sink
      • recycling:设置回收的方式。
  • combiner:合并/组装器,对多条item流进行打包或组装。 image.png

    • combiner
      • combine:设置组合的方式
        • pack:打包
        • join:连接
        • batch:分组
      • components list:组成成分的比例
        • 默认是input port1 传输过来的item是1;
        • 设置其他input port的量,即可做到1:n的比例进行打包。上图为1:5进行打包。
  • separator:拆分器,需要与combiner配套使用。

    image.png

    • unpack:解包
    • split:分解

task executers

  • operator
  • transport
  • robot
  • ASRSvehicle

conveyors

  • straight conveyor:传送带 image.png
    • 两个传送带只要放在一起,就会自动拼接。
    • 传送带有直线和曲线两种,按需使用即可。

warehousing

  • rack:货架。

    image.png

    • storage object
      • 设置一些存储参数
    • dimensions
      • 调整货架的大小
      • number of bays:一行几格
      • number of levels:一行几层
      • slots per bay:一格有几个槽位

triggers

image.png

如上图所示,processor的触发器是有很多种的。下面介绍最常用的触发on entry(item流入时)、on exit(item流出时)、on message(接收信息时)。至于on reset,因为有全面的reset设置,因此博主没有特别对实例设置该触发。

  • on entry / on exit image.png

    • 在触发设置中,flexsim已经为使用者提供了许多预设好的选择。每一种设置看名字就能知道大概。
    • data:主要是设置item的label,但在设置之前,需要新增自定义的label。后面会对label设置进行讲解。
    • visual:设置item的视觉表现,例如换色等。当我们需要一个source发出几种不同颜色box时,就需要设置visual。
    • 其余自行探索。
  • on message image.png

    • on message触发中,预设选项与前两者不同,主要是对ports的控制以及shape的改变。通过这些设置,我们可以实现流入流出的停止与开始,以及模拟“加工改造”。
    • 但光靠预设,可能无法满足我们的需求,因此我们要学习flexsim内置触发的代码结构。
  • trigger code:点击触发栏目的“书页”(+×的后面)按钮,就可以打开code页面。

    • 基础变量:on entry/exit
    Object current = ownerobject(c);  // current表示当前的实例,比如你设置source的触发,那么current就表示source。
    Object item = param(1); // item就是从current流经
    int port = param(2); // port就是端口
    // 博主没有使用过port参数,各位可以自行探索。至于如何控制端口的开关,有其他函数。
    
    • 基础变量:on message
    Object current = ownerobject(c); // 同上
    Object fromObject = param(1); // fromObject:消息从哪个实例来,对应发布消息的实例
    Variant msgparam1 = param(2); // 参数,对应sendmessage()函数。
    Variant msgparam2 = param(3);
    Variant msgparam3 = param(4);
    
    // 使用sendmessage需要指定发送的实例对象(实例的名字),随后传入参数,数量可自定。
    // 接受message的实例,就需要设置on message的触发。
    
    • flexsim的函数,在网上没有看到比较好的整合。博主在学习过程中,主要信息来源于flexsim内置的函数声明(参数+返回)。
    • flexsim支持写入脚本,支持c++和java。如果会用相关语言,可以尝试一二。而在触发的代码中,与c语言类似。每一句以分号;结尾,if(){}的判断结构依旧可以使用。
    • 对于简单的仓储而言,最重要的数据记录,即进来多少出去多少,合格多少,不合格多少。在这些过程中,触发代码只需要设置变量,对其进行赋值与增减就行了。但我们都知道,局部变量是无法作用于全局的,所以在触发代码之外,我们还需要设置一些全局变量。

toolbox延伸

  • 标签设置 - flowitem bin

    • 在toolbox中,双击flowitem bin中的box。 image.png
    • 在右侧的labels栏目中,点击“加号”,即可新增label。label其实就是box的属性,你可以新增数字、字符串、指针等属性。
    • 在这里新增label后,你在模型设计中,就能针对该属性进行赋值、取值等操作。
    • 知道这一步之后,你可以往后延伸出非常多的可能。
  • 全局变量 - global variable

    • 在toolbox中除了固定的内容外,还可以新增其他内容。在modeling logic中,我们可以新增全局变量。

    image.png

    • 设置全局变量的name、type,对其进行简短的描述,以及设定其初始值。建议在此设定好你需要的初始值,每次reset后,该值将重置为你设定的初始值。

    image.png

  • 模型触发 - model trigger

    • 同样在modeling logic项目中,我们可以新增model trigger(模型触发)。在实例中,可以设置触发器;在模型中,也可以设置触发。
      • on model open
      • on reset
      • on run start
      • on run stop

    image.png

    • 选择on reset触发,里面是空白的,需要我们自行编写代码。

项目介绍

在相关链接中,我已经附上了github地址。里边有建立好的flexsim模型文件,简单服务端的golang代码,若不用go语言,可直接使用exe文件打开本地服务器。

下面将从无到有,构建该项目。

模型介绍

该模型为简易的仓储模型。该仓库将接受服务器发送的数据,完成存储与输出的任务指标。其最终成品如下图。

image.png

从一个入口,可以输入红蓝绿三种颜色的box。对输入的box进行打标、质检、打包、赋rfid、按需存储、分拣、按需出仓。

  • 模型从上位机接收参数:
    • 输入的box数量
    • 需求储存的pack数量
    • 需求出库三色box数量
  • 模型传递给上位机的参数:
    • 打标总量
    • 质检结果(合格/不合格数量)
    • 存储的pack数量
    • 存储的三色box数量

变量定义

  • 接收变量
    • input:将要入仓的box数量,inputsource只能输出这么多box。
    • packNeed:需要存储的pack
    • redNeed:需求出仓的red数量
    • blueNeed:需求出仓的blue数量
    • greenNeed:需求出仓的green数量
  • 传递变量
    • redNum:已经分拣的red存储数量
    • blueNum:已经分拣的blue存储数量
    • greenNum:已经分拣的green存储数量
    • labelNum:入仓打标的数量(无丢货的情况,与input相等)
    • unqNum:不合格
    • quaNum:合格
    • packNum:存储的pack数量
  • 其他变量
    • socket:int类型,socket连接的记录。
    • RFID:赋RFID的数量

功能分解

  • 输入模拟:生成三种颜色的box
    • 对box赋type标签,在生成的时候对item.type赋值
    • 根据item.type的值调整颜色
    • 每生成一个box,input--,当input==0时,使用closeoutput(current)关闭出口。

image.png

  • 打标
    • 在打标机的on exit触发中,更新labelNum,同时更新box的label标签即可。
    • 注意:需要先新增box标签,设置名字为label。
    • setlabelnum:设置item名为“label”的num型标签的值为labelNum。

image.png

  • 质检
    • 质检只能模拟,无法真的去检测什么。
    • 我们可以按概率,设置10%的不合格率。设置如下图。
    • 记录合格与不合格,只需要在后面的queue中设置on entry触发,在触发中更新对应的全局变量即可。

image.png

  • 打包

    • combiner的两个输入,port1为pallet托盘,port2为box,比例为1:8。如何设置已经在上文中介绍,不多赘述。
  • RFID

    • 与打标类似,唯一需要注意的,就是给pallet新增一个对应的标签。
  • pack存入rack

    • 在采用传送带进行传递时,可能会遇到一点问题,因为点击传送带打开其属性,标签页基本都是空的,怎么都找不到use transport,也就导致s连接连了没用。但实际上,使用传送带作为输入时,我们要以传送带上的小方块作为对象。
    • 如下图,与堆垛机进行s连接时,一定要记得是用白色方块进行连接,然后双击方块,就能找到你要的内容。

image.png

  • pack出入库
    • 入库时,packNum++;
    • 出库时,packNum--;
    • 可添加判断
      • 若packNum<=packNeed,关闭出口;
      • 否则,打开出口。

image.png

  • 拆包

    • 不需要设置太多,连好出入端口就行。
  • 叉车分拣

    image.png

    • queue连接了三个rack;
    • 根据item的type设置output。

    image.png

  • box按需出仓

    • 三个rack基本一致
    • 出入更新redNum即可
    • 在出库的时候,redNeed--,当其为0时,关闭出口。

image.png

至此,我们已经完成了模型的搭建。只需要设定好对应的变量,即便不进行socket通信,整个模型也能正常的离线运行。

而对于socket通信,我们需要考虑以下两点。

  • 如何建立socket连接;
  • 如何时刻收发信息。

socket通信

要达成flexsim与上位机的socket通信,首先要搭建本机服务器。博主使用golang简单搭建了一个本地服务器。

func main() {
   fmt.Println("服务器开始监听")
   listen, err := net.Listen("tcp", "0.0.0.0:8888")
   if err != nil {
      fmt.Println("listen err=", err)
      return
   }
   defer listen.Close()
   //循环等待客户端来连接
   for {
      fmt.Println("等待客户端来连接")
      conn, err := listen.Accept()
      if err != nil {
         fmt.Println("Accept err=", err)
      } else {
         fmt.Printf("Accept suc con=%v 客户端ip=%v\n", conn, conn.RemoteAddr().String())
      }
      go process(conn)
   }
}

使用net包的listen方法,在8888端口建立tcp连接。在for循环中等待连接,连接成功后开启一个process协程。

func process(conn net.Conn) {
   defer conn.Close()
   conn.Write([]byte(fmt.Sprintf("input%dpack%dred%dblue%dgreen%d", input, packNeed, redNeed, blueNeed, greenNeed)))
   for {
      //创建一个切片
      buf := make([]byte, 1024)
      n, err := conn.Read(buf)
      if err != nil {
         if err == io.EOF {
            fmt.Println("the connetction is closed")
            conn.Close()
         } else {
            fmt.Printf("Read Error: %s\n", err)
         }
         return
      }
      //3.显示客户端发送的内容到服务器的终端
      fmt.Printf("客户端:%s 发送信息:%s。\n", conn.RemoteAddr().String(), string(buf[:n]))
   }
}

在process协程中,处理刚刚进行的连接。博主没有做可视化界面,也没有做实时输入的功能,只在最开始连接的时候向客户端发送指定参数,随后不断接收客户端发来的信息。

在onModelRest触发中,写入下列代码。

clientclose(socket);
socketinit();
socket = clientcreate();
if(clientconnect(socket, "127.0.0.1",8888))
{
	pt("Flexsim客户端连接成功\n");
	string receivemsg = clientreceive(socket,NULL,100,1); //保存从物理模型接收到的信息
	string num = stringcopy(receivemsg,6,3);
	input = stringtonum(num);
	num = stringcopy(receivemsg,13,2);
	packNeed = stringtonum(num);	
	num = stringcopy(receivemsg,18,2);
	redNeed = stringtonum(num);
	num = stringcopy(receivemsg,24,2);
	blueNeed = stringtonum(num);
	num = stringcopy(receivemsg,31,2);
	greenNeed = stringtonum(num);
	print(input);
}
else {
	pt("连接失败\n");
	return 0;
}

先关闭socket对应的连接,初始化socket,新建连接指向socket,检测连接。连接成功后pt打印字符串。通过使用clientreceivestringcopystringtonum三个函数对字符串进行处理。其中stringcopy提示被弃用,可换用string。substr。不换也能用。而且这两者有一个缺点,即都需要指定长度。这限制了取值的位置,颇为不便。博主没有发现其他更好的办法,留待读者去发掘了。

搞定了socket连接,只要开启服务端,reset时便可建立socket连接。接下来便是构建持续传递信息的结构。

可采取以下结构,source指向sink,设置时间间隔100。这样每过100时间,就能触发一次on exit触发器,调用clientsend向服务端发送字符串信息。最后,服务端打印接收的信息。

image.png

image.png

上述方法其实就是在某个触发中使用clientsend函数,读者可以根据模型情况,去构建消息传递结构,或者直接放在某个实例中也可。

结语

希望本文能对你有帮助。