根据需求定义“任务”是一个完整的业务搬运流程,整个流程涉及到多个机构(设备)分别动作执行多个步骤,所以依据前面的模型设计,需要把任务分解到多个连续的子任务(作业),未来通过顺序串联下达执行的方式来分步骤的完成任务的执行。

1.1. 仓库规划

同样,依据需求我们先来做几个仓库的规划设计,101位置是AGV的入库起始站台,102是提升机门口的入库工位,104是提升机1楼轿厢工位,同理504是提升机在5楼的轿厢工位。左边是5楼的货位区编码规则,完整好的货位号加上楼层编码05-01-01表示五楼的01-01货位,如下图:

有了图上的基本仓库规划设计,我们才能把一个任务依据设计进行分解,例如:任务编码100的搬运任务是把货物从101 入库工位搬运到05-01-01货位存放。

设计约定:

① 102、502为进提升机工位,103、503为出提升机工位,出和入的工位分开来解决任务冲突的问题(当然也可以先规划一个工位,随着程序迭代改进)。

② 提升机控制逻辑与电梯类似,到达指定楼层后会自动开门。

③ 电梯只有关上廊门才能提升/下降。

这样根据约定(前置条件)现在我们针对这个任务逐步分解成以下子任务(作业)如下表,对照早期的需求会发现有增加作业,实际项目中也是如此,早期的需求定义到实际编码的时候,需要根据实际的设备接口协议,规定等调整早期的需求定义。

序号

作业描述

执行设备

1

调度AGV101站台搬运托盘到102站台

1AGV

2

调度提升机到1楼并打开门

提升机

3

调度AGV102工位搬运到104工位并卸货

1AGV

4

调度空AGV104工位到103工位

1AGV

5

调度提升机关门

提升机

6

调度提升机1楼提升到5楼并开门

提升机

7

调度空AGV到504工位并载货

5AGV

8

调度AGV从504工位到503工位

5AGV

9

调度提升机关门

提升机

10

调度AGV从503工位搬运到05-01-01货位并卸货

5AGV

上面的分解比需求表增加了两个子任务“调度空AGV104工位到102工位”和“调度AGV504工位到502工位”,是与实际的AGV设备对接通信协议后增加的,AGV小车的控制系统不能识别“调度AGV提升机1楼门口工位到提升机并卸货,返回提升机门口工位”需求里“返回”提升机门口工位。依据设备的接口逻辑,只能把这个步骤再分解成两个步骤来执行。

上述分解提供一个简化分解的模型和思路,实际项目可能更为复杂或简单。

1.2. 分解代码实现

我们在后台任务管理里添加任务编码100、源地址101、目标地址05-01-01的任务,如下图:

同时admin.py里增加新增任务默认状态设置为未处理的代码,我们通过pk值是否为None来判断model是否是新增还是修改。

#Task模型的管理器 class TaskAdmin(admin.ModelAdmin):
... def save_model(self, request, obj, form, change): #新增任务默认状态设置为 未处理 if obj.pk==None:
            obj.State=1 obj.User=request.user return super().save_model(request, obj, form, change)

接下来,我们演示如何依据上面的分解逻辑用代码来实现把任务分解成子任务并保存到Job对应的表里,同时,把任务的状态从“未处理”更新到“处理完成”状态。这里需要注意的就是任务的分解和状态变更必须在一个事务里完成,系统不能存在任务状态已经变更到处理完成,但是没有对应的作业,也不能存在已有对应的作业任务状态仍然是未处理状态的情形,否则就会造成系统业务上的混乱,前面章节提到的事务应用就非常重要!

#Task模型的管理器 class TaskAdmin(admin.ModelAdmin):
...

     @atomic def task_decompose_action(self, request, queryset): for obj in queryset: #只处理状态等于未处理的任务 if obj.State==1:
                result=self.task_decompose(request,obj) if result:
                    self.message_user(request, str(obj.TaskNum) + " 处理成功.") else:
                    self.message_user(request, str(obj.TaskNum) + " 处理成功.")

    task_decompose_action.short_description = '处理所选的' + ' 任务' def task_decompose(self,request,obj):
        success=True try:
            job1=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":1,"Source":obj.Source,"Target":'102',"Executor":"AGV01","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job1.save()
            job2=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":2,"Source":None,"Target":'1',"Executor":"ELEVATOR","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) 
            job2.save()
            job3=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":3,"Source":"102","Target":'104',"Executor":"AGV01","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job3.save()
            job4=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":4,"Source":'104',"Target":'103',"Executor":"AGV01","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job4.save()
            job5=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":5,"Source":'1',"Target":'5',"Executor":"ELEVATOR","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})  
            job5.save()
            job6=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":6,"Source":'504',"Target":'503',"Executor":"AGV05","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job6.save()
            job7=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":7,"Source":'5',"Target":None,"Executor":"ELEVATOR","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) 
            job7.save()
            job8=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":8,"Source":'503',"Target":'05-01-01',"Executor":"AGV05","State":1,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job8.save() #子任务分解完成,并提交数据库 #更新任务的状态到“处理完成” obj.State=5 obj.save() except Exception:
            success = False return success

执行效果如下图:

1.3. 列表增加“作业数量”

任务分解完成后,列表除了显示状态变化之外,能够查看作业的分解数量,为此在Task Admin里增加job_count函数用来显示作业数量。

#Task模型的管理器 class TaskAdmin(admin.ModelAdmin): #listdisplay设置要显示在列表中的字段(TaskId字段是Django模型的主键) list_display = ('TaskId','TaskNum', 'Source', 'Target', 'Barcode','State','PriorityColor','BeginDate','EndDate','job_count','task_operate',)

... def job_count(self, obj): return obj.job_set.count()

    job_count.short_description = '作业数量'

1.4. 编辑页面查看作业数据

Job模型外键管理Task模型,我就可以采用TabularInline的方式来显示任务的作业信息,通过编辑界面直接查看对应任务的Job列表。

class JobInline(admin.TabularInline):
    model = Job
    fields = ('TaskNum','OrderNo','Source', 'Target','Executor','State','BeginDate','EndDate',)
    extra = 0 #默认所有条目 # 只读的字段  readonly_fields = ('TaskNum','OrderNo','Source', 'Target','Executor','State','BeginDate','EndDate',) def has_delete_permission(self, request, obj=None): return False #不允许删除 def has_add_permission(self, request, obj=None): return False #不允许添加 #Task模型的管理器 class TaskAdmin(admin.ModelAdmin): #listdisplay设置要显示在列表中的字段(TaskId字段是Django模型的主键) list_display = ('TaskId','TaskNum', 'Source', 'Target', 'Barcode','State','PriorityColor','BeginDate','EndDate','job_count','task_operate',)
inlines=[JobInline]
fieldsets = (("任务", {'fields': ['TaskNum', ('Source', 'Target'), 'Barcode','Priority','State','BeginDate','EndDate','User']}),)

1.5. 魔法数(mogic number)

任务的状态赋值和变更我们采用了直接obj.State=1或obj.State=5的直接赋值数字的方式,这个就是程序的mogic number它们有特殊的含义,编程人员得知道这个数字对应的含义,当状态变多时间拉长,编程人员就得去回忆或来回查看数字对应的状态。好的编程习惯就是用常量来代替魔法数字。重构models.py里的Task和Job代码用常量替换mogic number.

class Task(models.Model):
    STATE_NEW =1 STATE_PROCESSED=4 STATE_RUNNING=5 STATE_COMPLETED=99 STATE_CANCEL=-1 TASK_STATE=((STATE_NEW,u'未处理'),(STATE_PROCESSED,u'处理成功'),(STATE_RUNNING,u'执行中'),(STATE_COMPLETED,u'完成'),(STATE_CANCEL,u'已取消'))

    TaskId = models.AutoField(u'ID',primary_key=True, db_column='task_id')
    TaskNum = models.IntegerField(u'任务号', null=False, db_column='task_num')
    Source = models.CharField(u'源地址', null=False, max_length=50, db_column='source')
    Target = models.CharField(u'目标地址', null=False, max_length=50, db_column='target')
    Barcode = models.CharField(u'容器条码', null=False, max_length=50, db_column='barcode')
    State = models.IntegerField(u'状态', choices=TASK_STATE, null=False, db_column='state')
    Priority = models.IntegerField(u'优先级', choices=PRIORITY, null=True, db_column='priority')
    BeginDate = models.DateTimeField(u'开始时间',null=True, db_column='begin_date')
    EndDate = models.DateTimeField(u'结束时间',null=True, db_column='end_date')
    SystemDate = models.DateTimeField(u'系统时间', null=False, auto_now_add=True, db_column='system_date')
    User = models.ForeignKey(User, verbose_name="操作员",null=True, on_delete=models.CASCADE,db_column='user_id') class Meta:
        db_table = 'task_task' ordering = ['-Priority','TaskId']
        verbose_name = verbose_name_plural = "任务" ... class Job(models.Model):

    STATE_NEW =1 STATE_START=2 STATE_COMPLETED=99 STATE_CANCEL=-1 JOB_STATE=((STATE_NEW,u'新作业'),(STATE_START,u'下达执行'), (STATE_COMPLETED,u'完成'),(STATE_CANCEL,u'已取消'))

凡是有魔法数字的地方都尽量替换成可以阅读的常量,采用中式英语都是可以推荐的方式:)。

class TaskBiz(object): """description of class""" def task_start(self,obj):
        success=False if obj.State==Task.STATE_PROCESSED:
            obj.State=Task.STATE_RUNNING try:
                obj.save()
                success = True except Exception:
                success = False return success 

... def task_decompose(self,request,obj):
        success=True try:
            job1=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":1,"Source":obj.Source,"Target":'102',"Executor":"AGV01","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job1.save()
            job2=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":2,"Source":None,"Target":'1',"Executor":"ELEVATOR","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) 
            job2.save()
            job3=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":3,"Source":"102","Target":'104',"Executor":"AGV01","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job3.save()
            job4=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":4,"Source":'104',"Target":'103',"Executor":"AGV01","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job4.save()
            job5=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":5,"Source":'1',"Target":'5',"Executor":"ELEVATOR","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})  
            job5.save()
            job6=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":6,"Source":'504',"Target":'503',"Executor":"AGV05","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job6.save()
            job7=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":7,"Source":'5',"Target":None,"Executor":"ELEVATOR","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) 
            job7.save()
            job8=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":8,"Source":'503',"Target":'05-01-01',"Executor":"AGV05","State":Job.STATE_NEW,\ "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,})
            job8.save() #子任务分解完成,并提交数据库 #更新任务的状态到“处理完成” obj.State=Task.STATE_PROCESSED
            obj.save() except Exception:
            success = False return success

1.6. 小结

本章节我们讲述了如何通过admin.py来快速的完成页面功能的构建,并通过自定义action快速的实现了任务分解功能,并根据业务进展也逐步的完善了查看页面以内联表的方式显示作业详情。随着业务功能的增加admin.py的代码逐步增多和变得复杂,下一章我们演示如何通过功能内聚和重构代码,增加代码可读性和可维护性。

python工业互联网应用实战6的更多相关文章

  1. 使用Python封装excel操作指南

    前言openpyxl 是 python 中操作 excel 表格比较常用的一个库,可以读取和写入excel文件,支持【.xlsx / .xlsm / .xltx / .xltm】格式的文件,处理excel数据、公式、样式,且可以在表格内插入图表但是在实际项目的使用过程中,如果经常要用到 openpy......

  2. python中openpyxl和xlsxwriter对Excel的操作方法

    前几天,项目中有个小需求:提供Excel的上传下载功能,使用模块:openpyxl 和 xlsxwriter,这里简单记录一下。1.简介Python中操作Excel的库非常多,为开发者提供了多种选择,如:xlrd、 xlwt、xlutils、xlwings、pandas、 win32com、open......

  3. Python jieba库分词模式实例用法

    在中文分词中,jiebe库是最为常见的,主要的原因还是它独特的支持分词模式如:精确模式、全模式、搜索引擎模式。也对应着三种方式,包括jieba.cut()方法、jieba.lcut()方法、jieba.cut_for_search()方法。下面就为大家实例操作这些分词模式,以及方法的使用,一起来了解......

  4. Python3利用scapy局域网实现自动多线程arp扫描功能

    一、所需Python库from scapy.all import *import threading二、实现ip扫描1.获取c段ip地址在ARP()里面有ip地址,我们可以从里面提取出前3段出来ARP().show()然后通过从后查找最后一个.得到最后一段位数,然后总长度-最后一段长度就能取出前3段......

  5. Python爬虫爬取全球疫情数据并存储到mysql数据库的步骤

    思路:使用Python爬虫对腾讯疫情网站世界疫情数据进行爬取,封装成一个函数返回一个 字典数据格式的对象,写另一个方法调用该函数接收返回值,和数据库取得连接后把 数据存储到mysql数据库。一、mysql数据库建表CREATE TABLE world(id INT(11) N......

  6. Python爬虫系统化学习(4)

    Python爬虫系统化学习(4)在之前的学习过程中,我们学习了如何爬取页面,对页面进行解析并且提取我们需要的数据。在通过解析得到我们想要的数据后,最重要的步骤就是保存数据。一般的数据存储方式有两种:存储在文件或者数据库中。在本篇博客中我会细致的讲解从零基础到血会存储在txt或者csv文件,以及通过P......

  7. 利用python为PostgreSQL的表自动添加分区

    PostgreSQL引进“分区”表特性,解放了之前采用“表继承”+“触发器”来实现分区表的繁琐、低效。而添加分区,都是手动执行SQL。 演示目的:利用python来为PostgreSQL的表自动添加分区。python版本:python3+ pip3 install psycopg2 一......

  8. python实现自幂数的示例代码

    1、什么是自幂数?前文介绍过 python 实现水仙花数,其实水仙花数为自幂数的一种,即,3位自幂数。自幂数是指一个 n 位数,它的每个位上的数字的 n 次幂之和等于它本身。(例如:当n为3时,有1^3 + 5^3 + 3^3 = 153,153即是n为3时的一个自幂数)自幂数-百度百科2、自幂......

  9. python中用ggplot绘制画图实例讲解

    Python的绘图库也允许用户创建优雅的图形,本章给大家介绍的是关于ggplot绘制画图的技巧,ggplot2建立在grid系统上,这个系统不支持纹理。需要额外创建一堆数据,再基于这些数据构建一个geom_path图层,盖在柱图上才可以进行各种绘制,下面给大家详细讲解下怎么使用ggplot绘图。简介......

  10. python 装饰器的基本使用

    知识点简单的装饰器带有参数的装饰器带有自定义参数的装饰器类装饰器装饰器嵌套@functools.wrap装饰器使用基础使用简单的装饰器def my_decorator(func):def wrapper():print('wrapper of decorator')func()return wrap......

随机推荐

  1. Python数据结构-集合

    1.集合"""集合(set):没有重复元素且没有顺序的数据结构定义语法:s = set({}) #空集合s = set({1, 2, 3, 4, 5})增加:add() 往集合添加一条数据update() 合并,支持传入列表、字典、元......

  2. python如何实现单向链表及单向链表的反转

    链表的定义链表中的每个节点会存储相邻节点的位置信息,单链表中的每个节点只存储下一关节点的位置信息单向链表的实现class ListNode:def __init__(self, val):self.val = valself.next = None要实现单向链表只需要把几个节点关联起来就可以了,把一......

  3. C#处理医学图像(一):基于Hessian矩阵的血管肺纹理骨骼增强对比

    在医院实际环境中,经常遇到有问题的患者,对于一些特殊的场景,比如骨折,肺结节,心脑血管问题需要图像对比增强来更为清晰的显示病灶助于医生确诊,先看效果:肺纹理增强:肺结节增强:血管对比增强:骨骼对比增强:根据参考资料:MATLAB版本:https://ww2.mathworks.cn/matlabce......

  4. linux显示行数命令

    linux 如何显示一个文件的某几行(中间几行)【一】从第3000行开始,显示1000行。即显示3000~3999行cat filename | tail -n +3000 | head -n 1000【二】显示1000行到3000行cat filename| head -n 3000 | tail......

  5. ASP.NET Core获取请求完整的Url

    在ASP.NET项目中获取请求完整的Url:获取System.Web命名空间下的类名为HttpRequestBase的Url方法:/// 在派生类中替代时,获取有关当前请求的 URL 的信息。 /// 包含有关当前请求的 URL 的信息的对象。 /// /// 始终。 /// public vi......

  6. Java中Jackson快速入门

    Java生态圈中有很多处理JSON和XML格式化的类库,Jackson是其中比较著名的一个。虽然JDK自带了XML处理类库,但是相对来说比较低级,使用本文介绍的Jackson等高级类库处理起来会方便很多。引入类库由于Jackson相关类库按照功能分为几个相对独立的,所以需要同时引入多个类库,为了方便......

  7. java编译命令基础知识点

    我们在对计算机下达指令时,人类的语言它是不能够明白,需要通过编译的时候翻译成计算机能听懂的语言。编译过程中会调用javac命令,这点大家可能接触的不多,毕竟是是计算机程序内部运行时的操作。下面我们就编译的概念、命令带来讲解,然后分享一个编译实例给大家练习。1.编译概念通过流程图可以看出其实java的......

  8. java读取证书公钥的实现

    方式1:使用javax.security.cert.X509Certificate进行解析URL url = Demo.class.getClassLoader().getResource("C000024.crt"); //证书路径System.out.println(&qu......

  9. python re模块和正则表达式

    一、re模块和正则表达式先来看一个例子:https://reg.jd.com/reg/person?ReturnUrl=https%3A//www.jd.com/这是京东的注册页面,打开页面我们就看到这些要求输入个人信息的提示。假如我们随意的在手机号码这一栏输入一个11111111111,它会提示我......

  10. vue中怎么动态生成form表单

    快速生成一个具有动态渲染、数据收集、验证和提交功能的表单生成组件。支持3个UI框架,并且支持生成任何 Vue 组件。form-create 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的表单生成组件。支持3个UI框架,并且支持生成任何 Vue 组件。内置20种常用表单组件和自......