根据需求定义“任务”是一个完整的业务搬运流程,整个流程涉及到多个机构(设备)分别动做执行多个步骤,因此依据前面的模型设计,须要把任务分解到多个连续的子任务(做业),将来经过顺序串联下达执行的方式来分步骤的完成任务的执行。数据库
一样,依据需求咱们先来作几个仓库的规划设计,101位置是AGV的入库起始站台,102是提高机门口的入库工位,104是提高机1楼轿厢工位,同理504是提高机在5楼的轿厢工位。左边是5楼的货位区编码规则,完整好的货位号加上楼层编码05-01-01表示五楼的01-01货位,以下图:编程
有了图上的基本仓库规划设计,咱们才能把一个任务依据设计进行分解,例如:任务编码100的搬运任务是把货物从101 入库工位搬运到05-01-01货位存放。函数
设计约定:编码
① 10二、502为进提高机工位,10三、503为出提高机工位,出和入的工位分开来解决任务冲突的问题(固然也能够先规划一个工位,随着程序迭代改进)。atom
② 提高机控制逻辑与电梯相似,到达指定楼层后会自动开门。spa
③ 电梯只有关上廊门才能提高/降低。设计
这样根据约定(前置条件)如今咱们针对这个任务逐步分解成如下子任务(做业)以下表,对照早期的需求会发现有增长做业,实际项目中也是如此,早期的需求定义到实际编码的时候,须要根据实际的设备接口协议,规定等调整早期的需求定义。code
序号orm |
做业描述blog |
执行设备 |
1 |
调度AGV从101站台搬运托盘到102站台 |
1楼AGV |
2 |
调度提高机到1楼并打开门 |
提高机 |
3 |
调度AGV从102工位搬运到104工位并卸货 |
1楼AGV |
4 |
调度空AGV从104工位到103工位 |
1楼AGV |
5 |
调度提高机关门 |
提高机 |
6 |
调度提高机1楼提高到5楼并开门 |
提高机 |
7 |
调度空AGV到504工位并载货 |
5楼AGV |
8 |
调度AGV从504工位到503工位 |
5楼AGV |
9 |
调度提高机关门 |
提高机 |
10 |
调度AGV从503工位搬运到05-01-01货位并卸货 |
5楼AGV |
上面的分解比需求表增长了两个子任务“调度空AGV从104工位到102工位”和“调度AGV从504工位到502工位”,是与实际的AGV设备对接通讯协议后增长的,AGV小车的控制系统不能识别“调度AGV从提高机1楼门口工位到提高机并卸货,返回提高机门口工位”需求里“返回”提高机门口工位。依据设备的接口逻辑,只能把这个步骤再分解成两个步骤来执行。
上述分解提供一个简化分解的模型和思路,实际项目可能更为复杂或简单。
咱们在后台任务管理里添加任务编码100、源地址10一、目标地址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
执行效果以下图:
任务分解完成后,列表除了显示状态变化以外,可以查看做业的分解数量,为此在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 = '做业数量'
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']}),)
任务的状态赋值和变动咱们采用了直接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
本章节咱们讲述了如何经过admin.py来快速的完成页面功能的构建,并经过自定义action快速的实现了任务分解功能,并根据业务进展也逐步的完善了查看页面之内联表的方式显示做业详情。随着业务功能的增长admin.py的代码逐步增多和变得复杂,下一章咱们演示如何经过功能内聚和重构代码,增长代码可读性和可维护性。