agv调度开发
This commit is contained in:
@@ -82,7 +82,7 @@ class AgvScheduling(models.Model):
|
||||
# 计算agv_scheduling.workorder_ids与workorders的交集
|
||||
repetitive_workorders = agv_scheduling.workorder_ids & workorders
|
||||
raise RepeatTaskException(
|
||||
'制造订单号【%s】已存在与【%s】AGV调度任务,请勿重复下发!' %
|
||||
'制造订单号【%s】已存在于【%s】AGV调度任务,请勿重复下发!' %
|
||||
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
|
||||
)
|
||||
|
||||
@@ -98,20 +98,23 @@ class AgvScheduling(models.Model):
|
||||
('route_type', '=', agv_route_type),
|
||||
('start_site_id', '=', agv_start_site_id)
|
||||
])
|
||||
idle_route = None
|
||||
if len(agv_routes) == 1:
|
||||
vals.update({'end_site_id': agv_routes[0].end_site_id.id, 'agv_route_name': agv_routes[0].name})
|
||||
idle_route = agv_routes[0]
|
||||
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_name': idle_route.name})
|
||||
else:
|
||||
# 判断终点接驳站是否为空闲
|
||||
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
|
||||
if idle_routes:
|
||||
# 将空闲的路线按照终点接驳站名称排序
|
||||
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
|
||||
vals.update({'end_site_id': idle_routes[0].end_site_id.id, 'agv_route_name': idle_routes[0].name})
|
||||
idle_route = idle_routes[0]
|
||||
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_name': idle_route.name})
|
||||
try:
|
||||
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
|
||||
# 触发空闲接驳站状态更新,触发新任务下发
|
||||
if scheduling.end_site_id.state == '空闲':
|
||||
scheduling.dispatch_scheduling(scheduling.end_site_id.id, scheduling.end_site_id.state)
|
||||
if idle_route and idle_route.end_site_id.state == '空闲':
|
||||
scheduling.dispatch_scheduling(idle_route)
|
||||
|
||||
except Exception as e:
|
||||
_logger.error('添加AGV调度任务失败: %s', e)
|
||||
@@ -134,8 +137,11 @@ class AgvScheduling(models.Model):
|
||||
order='id asc',
|
||||
limit=1
|
||||
)
|
||||
task_route = task_routes.filtered(
|
||||
lambda r: r.start_site_id == agv_scheduling.start_site_id and r.start_site_id == agv_scheduling.start_site_id
|
||||
)
|
||||
# 下发AGV调度任务并修改接驳站状态为占用
|
||||
agv_scheduling.dispatch_scheduling(agv_site_id, agv_site_state)
|
||||
agv_scheduling.dispatch_scheduling(task_route)
|
||||
else:
|
||||
# 如果终点接驳站变为占用,则认为任务完成
|
||||
agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
|
||||
@@ -230,21 +236,33 @@ class AgvScheduling(models.Model):
|
||||
rec.state = '已配送'
|
||||
rec.task_completion_time = fields.Datetime.now()
|
||||
|
||||
def dispatch_scheduling(self, agv_end_site_id, agv_site_state):
|
||||
def dispatch_scheduling(self, agv_task_route):
|
||||
"""
|
||||
下发调度任务
|
||||
params:
|
||||
agv_route sf.agv.task.route对象
|
||||
"""
|
||||
for rec in self:
|
||||
if rec.state != '待下发':
|
||||
return False
|
||||
# rec._delivery_avg()
|
||||
# 获取agv任务路线名称
|
||||
rec.state = '配送中'
|
||||
rec.task_delivery_time = fields.Datetime.now()
|
||||
rec.site_state = agv_site_state
|
||||
rec.end_site_id = agv_end_site_id
|
||||
rec.site_state = '空闲'
|
||||
rec.end_site_id = agv_task_route.end_site_id.id
|
||||
rec.agv_route_name = agv_task_route.name
|
||||
# 更新接驳站状态
|
||||
rec.env['sf.agv.site'].update_site_state({rec.end_site_id.name: '占用'}, False)
|
||||
|
||||
def write(self, vals):
|
||||
if vals.get('state', False):
|
||||
if vals['state'] == '已取消':
|
||||
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({'status': '待下发'})
|
||||
elif vals['state'] == '已配送':
|
||||
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({'status': '已配送'})
|
||||
return super().write(vals)
|
||||
|
||||
|
||||
class ResMrpWorkOrder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
|
||||
@@ -650,6 +650,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
'production_id': self.production_id.id,
|
||||
'production_line_id': self.production_id.production_line_id.id,
|
||||
'type': '上产线',
|
||||
'is_cnc_program_down': True
|
||||
# 'route_id': up_route.id,
|
||||
# 'feeder_station_start_id': agv_start_site_id,
|
||||
# 'feeder_station_destination_id': up_route.end_site_id.id
|
||||
@@ -1254,7 +1255,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
button_element = etree.Element('button', {
|
||||
'name': 'button_delivery',
|
||||
'type': 'object',
|
||||
'string': '拆卸',
|
||||
'string': '解除装夹',
|
||||
'class': 'btn-primary',
|
||||
# 'className': 'btn-primary',
|
||||
'modifiers': '{"force_show": 1}'
|
||||
@@ -1290,7 +1291,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
'default_delivery_type': delivery_type,
|
||||
'default_workorder_ids': [(6, 0, workorder_ids)],
|
||||
'default_workcenter_id': self.env.context.get('default_workcenter_id'),
|
||||
'default_confirm_button': '确认拆卸'
|
||||
'default_confirm_button': '确认解除'
|
||||
}}
|
||||
|
||||
|
||||
@@ -1580,12 +1581,14 @@ class WorkPieceDelivery(models.Model):
|
||||
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
|
||||
status = fields.Selection(
|
||||
[('待下发', '待下发'), ('已下发', '已下发'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
|
||||
default='待下发',
|
||||
tracking=True)
|
||||
default='待下发', tracking=True)
|
||||
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
|
||||
is_manual_work = fields.Boolean('人工操作', default=False)
|
||||
active = fields.Boolean(string="有效", default=True)
|
||||
|
||||
agv_scheduling_id = fields.Many2one('sf.agv.scheduling', 'AGV任务调度')
|
||||
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if vals.get('route_id') and vals.get('type') is None:
|
||||
@@ -1634,7 +1637,7 @@ class WorkPieceDelivery(models.Model):
|
||||
|
||||
# 工件配送
|
||||
def button_delivery(self):
|
||||
# delivery_ids = []
|
||||
delivery_ids = []
|
||||
production_ids = []
|
||||
workorder_ids = []
|
||||
is_cnc_down = 0
|
||||
@@ -1654,7 +1657,7 @@ class WorkPieceDelivery(models.Model):
|
||||
if item.is_cnc_program_down is False:
|
||||
is_cnc_down += 1
|
||||
if is_cnc_down == 0 and is_not_production_line == 0:
|
||||
# delivery_ids.append(item.id)
|
||||
delivery_ids.append(item.id)
|
||||
production_ids.append(item.production_id.id)
|
||||
workorder_ids.append(item.workorder_id.id)
|
||||
if is_cnc_down >= 1:
|
||||
@@ -1668,7 +1671,7 @@ class WorkPieceDelivery(models.Model):
|
||||
'res_model': 'sf.workpiece.delivery.wizard',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
# 'default_delivery_ids': [(6, 0, delivery_ids)],
|
||||
'default_delivery_ids': [(6, 0, delivery_ids)],
|
||||
'default_production_ids': [(6, 0, production_ids)],
|
||||
'default_delivery_type': delivery_type,
|
||||
'default_workorder_ids': [(6, 0, workorder_ids)],
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<xpath expr="//div/t[@t-if='nbSelected']" position="replace">
|
||||
<t t-if="nbSelected">
|
||||
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||
<t t-if="button.modifiers.force_show == 0">
|
||||
<t t-if="!button.modifiers.force_show">
|
||||
<ListViewHeaderButton
|
||||
list="model.root"
|
||||
clickParams="button.clickParams"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
decoration-danger="state == '配送中'"
|
||||
decoration-info="state == '已取消'"
|
||||
/>
|
||||
<field name="agv_route_type" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="agv_route_name"/>
|
||||
<field name="start_site_id"/>
|
||||
@@ -23,10 +24,11 @@
|
||||
<field name="task_delivery_time" readonly="1"/>
|
||||
<field name="task_completion_time" readonly="1"/>
|
||||
<field name="task_duration" readonly="1"/>
|
||||
|
||||
<button
|
||||
name="button_cancel"
|
||||
string="取消" type="object"
|
||||
attrs="{'invisible': [('state', '!=', '待下发')]}"
|
||||
attrs="{'invisible': ['|', ('state', '!=', '待下发'), ('agv_route_type', '=', '运送空料架')]}"
|
||||
icon="fa-times"
|
||||
class="btn-danger"
|
||||
confirm="你确定要取消这条记录吗?"
|
||||
|
||||
@@ -631,12 +631,12 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="工件配送" class="center" create="0" delete="0">
|
||||
<header>
|
||||
<button name="button_delivery" type="object" string="配送" class="btn-primary" attrs="{'force_show':1}"/>
|
||||
<button name="button_delivery" type="object" string="工件配送" class="btn-primary" attrs="{'force_show':1}"/>
|
||||
</header>
|
||||
<field name="status" widget="badge"
|
||||
decoration-success="status == '已配送'"
|
||||
decoration-warning="status == '待下发'"
|
||||
decoration-danger="status == '待配送'"
|
||||
decoration-danger="status == '已下发'"
|
||||
decoration-info="status == '已取消'"
|
||||
/>
|
||||
<field name="name"/>
|
||||
@@ -706,7 +706,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="工件配送">
|
||||
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
|
||||
<filter name="filter_waiting_delivery" string="待配送" domain="[('status', 'in', ['待配送'])]"/>
|
||||
<filter name="filter_issued" string="已下发" domain="[('status', 'in', ['已下发'])]"/>
|
||||
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
|
||||
<field name="rfid_code"/>
|
||||
<field name="production_id"/>
|
||||
@@ -730,7 +730,7 @@
|
||||
<field name="res_model">sf.workpiece.delivery</field>
|
||||
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
|
||||
<field name="context">{'search_default_filter_to_be_issued': 1,
|
||||
'search_default_filter_waiting_delivery': 1}
|
||||
'search_default_filter_issued': 1}
|
||||
</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</group>
|
||||
<footer>
|
||||
<button string="确认配送" name="confirm" type="object" class="oe_highlight" attrs="{'invisible': [('confirm_button', '!=', '确认配送')]}"/>
|
||||
<button string="确认拆卸" name="confirm" type="object" class="oe_highlight" attrs="{'invisible': [('confirm_button', '!=', '确认拆卸')]}"/>
|
||||
<button string="确认解除" name="confirm" type="object" class="oe_highlight" attrs="{'invisible': [('confirm_button', '!=', '确认解除')]}"/>
|
||||
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||
<script>
|
||||
setTimeout(function(){
|
||||
|
||||
@@ -13,6 +13,7 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
|
||||
# delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送')
|
||||
rfid_code = fields.Char('rfid码')
|
||||
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送单')
|
||||
workorder_ids = fields.Many2many('mrp.workorder', string='工单')
|
||||
production_ids = fields.Many2many('mrp.production', string='制造订单号')
|
||||
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
|
||||
@@ -70,17 +71,37 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
# if is_not_production_line >= 1:
|
||||
# raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
|
||||
# else:
|
||||
self.env['sf.agv.scheduling'].add_scheduling(
|
||||
scheduling = self.env['sf.agv.scheduling'].add_scheduling(
|
||||
agv_start_site_id=self.feeder_station_start_id.id,
|
||||
agv_route_type=self.delivery_type,
|
||||
workorders=self.workorder_ids,
|
||||
)
|
||||
# 如果关联了工件配送单,则修改状态为已下发
|
||||
if self.delivery_ids:
|
||||
self.delivery_ids.write({
|
||||
'status': '已下发',
|
||||
'agv_scheduling_id': scheduling.id,
|
||||
'feeder_station_start_id': scheduling.start_site_id.id,
|
||||
})
|
||||
|
||||
# 如果是解除装夹工单,则需要处理工单逻辑
|
||||
for item in self.workorder_ids:
|
||||
if item.routing_type == '解除装夹' and item.state == 'ready':
|
||||
item.button_start()
|
||||
item.button_finish()
|
||||
|
||||
# 显示通知
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'target': 'new',
|
||||
'params': {
|
||||
'message': '任务下发成功!AGV任务调度编号为【%s】' % scheduling.name,
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
'next': {'type': 'ir.actions.act_window_close'},
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logging.info('%s任务下发失败:%s' % (self.delivery_type, e))
|
||||
raise UserError('%s任务下发失败:%s' % (self.delivery_type, e))
|
||||
|
||||
Reference in New Issue
Block a user