首页 > 其他分享 >顺序控制和状态机之间的差别

顺序控制和状态机之间的差别

时间:2024-08-29 17:04:11浏览次数:11  
标签:count 差别 int recordsData 状态机 records step error 顺序控制

1. 背景

  • 在PLC一直以来的工程经验里面。如果想要做一个顺序控制(业务逻辑):
    • 用Graph实现
    • 用Csae..Of分支跳转语句
    • 用int数的变化切换不同的执行语句
  • 下面一种一种的来看,先看看使用int数切换实现一个顺序控制的案例:



    • 在这个案例里面,程序扫描currrent_step的值的不同,从而选择执行不同的程序段的内容,根据条件是否满足则可以做到在一个程序段里实现往下顺序执行,或者往上循环回跳直到满足跳出循环的条件。或者定点跳到指定步。这种方法的缺点是:1.可读性差;2.新增顺序控制的时候难以直接在原程序之间差入程序而不引入老程序逻辑的修改;3.在当前步执行的过程中,不知道前序步是从哪儿过来的,因为也许有好几个前序步在同时指向当前步;4. 本质还是IF判断,意味着每个循环内所有条件都被判断了一遍。好处就是简单好写,顺序编写,只管满足条件就跳转,不用过多考虑逻辑判断的冲突。
  • 如果用Case..Of来写,情况又会好一点:
    • Step_int用在了Switch语句里面,指定哪个分支就跳到那个分支,效率提高。
    • 但是缺点依旧,复杂程序步序很多,Step_int在分支语句中被重新赋值,但是不知道前序是哪里来的,也很难在不去改变Step_int值的情况下插入新的逻辑代码
//step
CASE #step_int OF
   
        //init start
    20:
       
        IF "enable_cycle" THEN
            
            #step_int := 21;
            
        END_IF;

        //cycle buffer
    21:
            "recordsData".buffer := "random_data".buffer[#random_cycle].buffer;
            #random_cycle := #random_cycle + 1;
            #step_int := 22;
            
            //cycle count
        22:
            IF #random_cycle < 4 THEN
                #step_int := 30;
            ELSIF #random_cycle = 4 THEN
                #random_cycle := 0;
                #step_int := 30;
            END_IF;
                   
        //way of ascii send to plc300
    30:
        "AsciiSendBuffer".buffer := "recordsData".buffer;
        "_cm1241data".P_SEND.Req := true;
        IF "_cm1241data".P_SEND.Done THEN
            #step_int := 40;
        END_IF;
        
        //tcp inform to 300plc-->inform ascii send finish
    40:
        "_cm1241data".P_SEND.Req := false;
        #step_int := 50;
        
        //tcp inform to 1200plc-->inform ascii rcv finish
    50:
        IF "_cm1241data".P_RCV.Ndr THEN
            #step_int := 60;
        END_IF;
        
        //wait for the feedback/compare 
    60:
        #my_time := T#200ms;
        #timeline.enable := true;
        IF #timeline.time_down AND NOT #impulse_array[1] THEN
            
            "recordsData".records.total_count := "recordsData".records.total_count + 1;
            IF "AsciiRcvBuffer".buffer <> "recordsData".buffer THEN
                "recordsData".records.result := 'N';
                #TmpInt := RD_LOC_T("recordsData".records.timestamp);
                IF "recordsData".records.error_count < 1024 THEN
                    "recordsData".records.table["recordsData".records.error_count].total_count := "recordsData".records.total_count;
                    "recordsData".records.table["recordsData".records.error_count].errror_count := "recordsData".records.error_count;
                    "recordsData".records.table["recordsData".records.error_count].time_stamp := "recordsData".records.timestamp;
                    "recordsData".records.error_count := "recordsData".records.error_count + 1;
                    #step_int := 70;
                ELSE
                    "recordsData".records.error_carry := "recordsData".records.error_carry + 1;
                    "recordsData".records.error_count := "recordsData".records.error_count := 0;
                    "recordsData".records.table["recordsData".records.error_count].total_count := "recordsData".records.total_count;
                    "recordsData".records.table["recordsData".records.error_count].errror_count := "recordsData".records.error_count;
                    "recordsData".records.table["recordsData".records.error_count].time_stamp := "recordsData".records.timestamp;
                    "recordsData".records.error_count := "recordsData".records.error_count + 1;
                    #step_int := 70;
                END_IF;
                
            ELSE
                "recordsData".records.result := 'Y';
                #TmpInt := RD_LOC_T("recordsData".records.timestamp);
                #step_int := 70;
            END_IF;
            
        END_IF;
        #impulse_array[1] := #timeline.time_down;
        
        //data clean
    70:
        #timeline.enable := FALSE;
        #step_int := 71;
        
        //rcv rst
    71:
        "_cm1241data".P_RST.REQ := true;
        #step_int := 72;
        
        
        //rst ok
    72:
        #rst_ton(IN:=(#step_int=72),
                 PT:=T#500ms);
        
        IF "_cm1241data".P_RST.DONE OR #rst_ton.Q THEN
            "_cm1241data".P_RST.REQ := false;
            #step_int := 80;
        END_IF;
        
        //next turn 
    80:
        RESET_TIMER(#rst_ton);
        #step_int := 21;
        
    END_CASE;
    
//execute
#Random_Instance(udintORchar:=true,
                 Out_char_random=>#generate_char);

#alternate_time_Instance(In_switchtime:=#my_time,
                         In_startAlternate:=#timeline.enable,
                         Out_switchtime_up=>#timeline.time_up,
                         Out_switchtime_Down=>#timeline.time_down);
  • 在这种Cse..Of的情况下,有方法可以做到记录跳转前序:
//1. 把Step_Int改成一个Current_Step,一个nextStep
//2. case依靠nextStep转换,步序转换赋值给currentStep
//3. 新建一个方法,方法在每个扫描周期都会被调用,方法作用是当发现currentStep和nextStep值不同时,把新的currentStep的值重新赋值给nextStep
//4. 这种方法还可以灵活的在转换间隔加上延时以控制延时时间,或者加入一些暂停,取消,终止,手动等等很多步序控制的新功能。
  • 西门子PLC其实提供了更好的做顺序控制的方法,如图:
    • 这是Graph,他有Step,也有Trans
    • 用户不用再去考虑分支转换的问题,只要在当前步,满足条件就可去到下一步或者指定步。
    • 有很多状态点可以知道程序的现在位置,和上一个位置,以及下一个位置
    • 状态图形化,直观。

2. 什么是状态机

  • 上面提到的都是关于顺序控制的,也就是说上面的控制一定是逐过程的。当新写一段Graph程序的时候,设计者通常考虑的都是:初始化状态是什么, 第一步要干什么,满足第一步条件之后要跳到哪一步去,下一步又要干嘛,结束和恢复状态条件是什么。这种更是一种边做边写的过程。步序从头到位以满足使用需要。
  • 状态机和顺控有一些简单的相似点,但是他们更多的是不同点。比如西门子的Graph就可以理解为一种状态机,这是一种“从进到出的”顺序,它一定有前序,每一步的执行或多或少是和一些前序步和前置条件绑定的。
  • 但是状态机的思想是:它更少的去关注过程(条件),更多的关注状态,每一种独立的行为或者动作就是一个状态。这个时候可以尽量的把有限的状态给统计起来,一个状态做一个方法。当写程序之前,首先画一画状态图,考虑当前状态要进入的条件,或者当前状态要出去的条件,注意的是,这种情况下我们其实仅仅在关注当前状态的前后条件。然后我们用Switch..Case把所有状态枚举出来。通过一个闲时状态统一管理状态的切换,每一个状态执行完毕之后都回到闲时状态,由它来决定下一步怎么走。
  • 和顺控不一样的地方出来了,顺口大部分是一步一步走,状态机无所谓上一步,它只会在Idle中去调用分支。对于一些事件触发(按钮事件),按下则触发响应的状态。
  • 如果状态机要做一个需要前后判断的状态动作,则可以在Idle状态里面去判断当前状态的标志位,下一个状态没有进行的标志位,如果标志位满足逻辑则进入。
  • 状态机最重要的是状态图,程序的修改要及时修改状态图。
  • 状态机的扩展仅仅只需要扩展一个新的状态,按照新的状态图改变即可
  • 有一些复杂的场景,状态机的状态表示甚至可以用一个二维数组的表示
  • 一个状态机的全周期,其实就可以理解为一个产品的全部功能。

标签:count,差别,int,recordsData,状态机,records,step,error,顺序控制
From: https://www.cnblogs.com/xiacuncun/p/18386867

相关文章

  • 【Java】IDEA从零到一使用statemachine状态机模拟订单 (图解)
    Java系列文章目录补充内容Windows通过SSH连接Linux第一章Linux基本命令的学习与Linux历史文章目录Java系列文章目录一、前言二、学习内容:三、问题描述四、解决方案:4.1认识依赖4.2使用状态机4.2.1目录结构4.2.2状态机解析4.2.2.1概念4.2.2.2图解4.2.2.3拓展......
  • async await 状态机理解
    publicasyncTask<string>Wait3S(){awaitTask.Delay(3000);Console.WriteLine("Wait3S");return"";}#region异步任务-状态机#iftrueTestClasstestClass=newTestClass();//调用testClass的Wait3S方法执行varWait3S=t......
  • TCPIP路由技术第一卷第八章OSPF 第二部分-1接口结构和接口状态机
    tcp/ip_ospf案例研究一r2:inte1/0ipospf110area0r3:inte1/0ipospf110area0r2:showipospfneighborauto-costreference-bandwidth更改缺省的参考带宽.在一个ospf域建议配置一致.inftransdelay:这个信息是指lsa从路由器的接口发送后经历的时间,以秒数计算,......
  • C# x Unity面向对象补全计划 设计模式 之 实现一个简单的有限状态机
    一个简单的有限状态机可以有如下内容1.状态基类(定义基本状态的方法,如进入(Enter)、执行(Execute)和退出(Exit),同时可以在此声明需要被管理的对象)2.具体状态类(定义具体状态,如:跳跃,行走,待机,每个具体状态类继承自状态基类)3.管理状态类(负责管理状态的切换逻辑,确保在不同状态之间进行......
  • Unity 麦扣 x 勇士传说 全解析 之 有限状态机(附各模块知识的链接,零基础也包学会的牢弟
            在编码前我一直有个疑问,为什么不是将方法写在一个一个类中,或者用的单例模式写个管理器来继承的方式来做怪物脚本,玩家控制和玩家动画控制的代码混在一起不说,与其他脚本之间的交互,让过于冗杂的代码不易阅读        这节开始应用的有限状态机,似乎一定......
  • 力扣 | 动态规划 | 状态机 | 买卖股票 | 买卖股票的最佳时机
    文章目录一、309.买卖股票的最佳时机含冷冻期二、714.买卖股票的最佳时机含手续费三、123.买卖股票的最佳时机III四、188.买卖股票的最佳时机IV本质上这种属于动态规划题目但又有多种状态的,我们只需要正确定义出状态即可。可能每个人定义的状态不一样,但只要是对......
  • 时下最火AI绘画工具comfyui和webui的差别和优劣势,选择最适合自己的AI工具
    ComfyUI简介:ComfyUI是一个高度模块化和灵活的用户界面,专注于提供复杂的图像处理和生成功能。它通常被用作高级用户和开发人员的工具。优点:高度模块化:ComfyUI允许用户通过模块化组件来构建自己的图像处理流程。用户可以自由组合和配置这些组件,以满足特定需求。......
  • 状态机设计
    目录状态机设计状态机概念什么是RTL级好的FSM描述?“一段式”写法“二段式”写法“二段式”描述方法“二段式”缺点以及解决办法代码“三段式”写法“三段式”的描述方法“三段式”的优点三种写法的优缺点独热码Moore和Mealy两种状态机的区别:主要是输出是否与输入有关例题分析(1)M......
  • Spring 状态机极简使用
    Spring状态机极简使用本文不探讨状态机的思想与Spring状态机的架构,仅做快速实现参考。Spring状态机官方文档项目参考代码基于SpringBoot配置的快速集成案例maven依赖配置<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-s......
  • 【unity实战】完美的2D横版平台跳跃玩家控制器,使用InputSystem+有限状态机实现人物加
    最终效果文章目录最终效果前言素材目录结构动画配置检测脚本状态机玩家有限状态机玩家控制脚本定义人物不同状态待机移动跳跃下落状态落地状态墙壁滑行状态蹬墙跳状态蹬墙跳下落状态一段近战攻击状态二段近战攻击状态冲锋状态土狼时间状态攀爬开始状态攀爬进行状态功能......