From a79500d0ad9d33211a3b1e27f66a6ad38f5c6d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Thu, 8 Aug 2024 17:47:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0agv=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/__manifest__.py | 1 + sf_manufacturing/data/agv_dispatch_data.xml | 12 ++ sf_manufacturing/models/agv_dispatch.py | 48 ++++-- sf_manufacturing/models/agv_setting.py | 8 +- sf_manufacturing/models/mrp_workorder.py | 149 ++++++++---------- sf_manufacturing/views/agv_setting_views.xml | 4 +- sf_manufacturing/views/mrp_workorder_view.xml | 12 +- .../wizard/workpiece_delivery_views.xml | 14 +- .../wizard/workpiece_delivery_wizard.py | 46 +++++- 9 files changed, 168 insertions(+), 126 deletions(-) create mode 100644 sf_manufacturing/data/agv_dispatch_data.xml diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index a4199939..f17a40df 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -15,6 +15,7 @@ 'data/stock_data.xml', 'data/empty_racks_data.xml', 'data/panel_data.xml', + 'data/agv_dispatch_data.xml', 'security/group_security.xml', 'security/ir.model.access.csv', 'wizard/workpiece_delivery_views.xml', diff --git a/sf_manufacturing/data/agv_dispatch_data.xml b/sf_manufacturing/data/agv_dispatch_data.xml new file mode 100644 index 00000000..32b76406 --- /dev/null +++ b/sf_manufacturing/data/agv_dispatch_data.xml @@ -0,0 +1,12 @@ + + + + + AGV调度 + sf.agv.dispatch + B%(year)s%(month)s%(day)s + 4 + + + + \ No newline at end of file diff --git a/sf_manufacturing/models/agv_dispatch.py b/sf_manufacturing/models/agv_dispatch.py index f82b3f07..74c981f1 100644 --- a/sf_manufacturing/models/agv_dispatch.py +++ b/sf_manufacturing/models/agv_dispatch.py @@ -1,20 +1,24 @@ -from odoo import models, fields, api -from odoo.exceptions import ValidationError +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + +import logging + +_logger = logging.getLogger(__name__) class AgvDispatch(models.Model): _name = 'sf.agv.dispatch' _description = 'agv调度' - name = fields.Char('任务单号') + name = fields.Char('任务单号', index=True, copy=False) def _get_agv_route_type_selection(self): return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection'] - agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型') + agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型', required=True) agv_route_name = fields.Char('任务路线名称') - start_site_id = fields.Many2one('sf.agv.site', '起点接驳站') - end_site_id = fields.Many2one('sf.agv.site', '终点接驳站') + start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True) + end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True) site_state = fields.Selection([ ('占用', '占用'), ('空闲', '空闲')], string='终点接驳站状态') @@ -22,7 +26,8 @@ class AgvDispatch(models.Model): ('待下发', '待下发'), ('配送中', '配送中'), ('已配送', '已配送'), - ('已取消', '已取消')], string='状态', default='待下发') + ('已取消', '已取消')], string='状态', default='待下发', tracking=True) + productions = fields.Char('制造订单', compute='_compute_productions') workpiece_delivery_ids = fields.One2many('sf.workpiece.delivery', 'agv_dispatch_id', string='工件配送单') delivery_workpieces = fields.Char('配送工件', compute='_compute_delivery_workpieces') @@ -44,7 +49,28 @@ class AgvDispatch(models.Model): else: rec.task_duration = '' - def add_queue(self, vals): - if not vals.get('agv_route_type') or vals.get('agv_route_type') not in self._get_agv_route_type_selection(): - raise ValidationError('无效的路线类型!') - self.env['sf.agv.dispatch'].sudo().create(vals) + @api.model_create_multi + def create(self, vals_list): + # We generate a standard reference + for vals in vals_list: + vals['name'] = self.env['ir.sequence'].next_by_code('sf.agv.dispatch') or _('New') + return super().create(vals_list) + + def add_queue(self, agv_start_site_id, agv_route_type, production_ids, delivery_ids): + """ + 根据参数增加AGV调度任务 + """ + vals = { + 'start_site_id': agv_start_site_id, + 'agv_route_type': agv_route_type, + 'productions': '、'.join(production_ids.mapped('name')), + 'workpiece_delivery_ids': delivery_ids or [], + 'task_create_time': fields.Datetime.now() + } + try: + dispatch = self.env['sf.agv.dispatch'].sudo().create(vals) + except Exception as e: + _logger.error('添加AGV调度任务失败: %s', e) + raise UserError(e) + + return dispatch diff --git a/sf_manufacturing/models/agv_setting.py b/sf_manufacturing/models/agv_setting.py index 833b2659..8fe22715 100644 --- a/sf_manufacturing/models/agv_setting.py +++ b/sf_manufacturing/models/agv_setting.py @@ -17,7 +17,7 @@ class AgvSetting(models.Model): ('空闲', '空闲')], string='状态') divide_the_work = fields.Char('主要分工') active = fields.Boolean('有效', default=True) - region = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True, + workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True, domain=[('is_agv_dispatch', '=', True)]) def update_site_state(self): @@ -73,14 +73,16 @@ class AgvTaskRoute(models.Model): if self.end_site_id == self.start_site_id: raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择") - region = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_dispatch', '=', True)], + workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_dispatch', '=', True)], compute="_compute_region") @api.depends('end_site_id') def _compute_region(self): for record in self: if record.end_site_id: - record.region = record.end_site_id.region + record.workcenter_id = record.end_site_id.workcenter_id + else: + record.workcenter_id = None class Center_controlInterfaceLog(models.Model): diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 060b06cf..792ecb9f 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -634,25 +634,33 @@ class ResMrpWorkOrder(models.Model): k, item), 'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k, item), - 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list( - production) + # 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list( + # production) }] return workorders_values_str - def _json_workpiece_delivery_list(self, production): - up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc') - down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc') + def _json_workpiece_delivery_list(self): + # 修改在装夹工单完成后,生成上产线的工件配送单 + + # up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc') + # down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc') return [ [0, '', - {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '上产线', - 'route_id': up_route.id, - 'feeder_station_start_id': up_route.start_site_id.id, - 'feeder_station_destination_id': up_route.end_site_id.id}], - [0, '', - {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线', - 'route_id': down_route.id, - 'feeder_station_start_id': down_route.start_site_id.id, - 'feeder_station_destination_id': down_route.end_site_id.id}]] + { + 'production_id': self.production_id.id, + 'production_line_id': self.production_id.production_line_id.id, + 'type': '上产线', + # 'route_id': up_route.id, + # 'feeder_station_start_id': agv_start_site_id, + # 'feeder_station_destination_id': up_route.end_site_id.id + } + ], + # [0, '', + # {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线', + # 'route_id': down_route.id, + # 'feeder_station_start_id': down_route.start_site_id.id, + # 'feeder_station_destination_id': down_route.end_site_id.id}] + ] # 拼接工单对象属性值(表面工艺) def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id): @@ -1182,6 +1190,8 @@ class ResMrpWorkOrder(models.Model): raw_move.write({'state': 'done'}) record.production_id.button_mark_done1() # record.production_id.state = 'done' + # 生成工件配送单 + record.workpiece_delivery_ids = record._json_workpiece_delivery_list() # 将FTP的检测报告文件下载到临时目录 def download_reportfile_tmp(self, workorder, reportpath): @@ -1485,7 +1495,7 @@ class SfWorkOrderBarcodes(models.Model): class WorkPieceDelivery(models.Model): _name = "sf.workpiece.delivery" - _inherit = ['mail.thread', 'mail.activity.mixin'] + _inherit = ['mail.thread', 'mail.activity.mixin', "barcodes.barcode_events_mixin"] _description = '工件配送' name = fields.Char('单据编码') @@ -1501,11 +1511,14 @@ class WorkPieceDelivery(models.Model): feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站') task_delivery_time = fields.Datetime('任务下发时间') task_completion_time = fields.Datetime('任务完成时间') - type = fields.Selection( - [('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型') + + def _get_agv_route_type_selection(self): + return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection'] + + type = fields.Selection(selection=_get_agv_route_type_selection, string='类型') delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration') status = fields.Selection( - [('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态', + [('待下发', '待下发'), ('已下发', '已下发'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态', default='待下发', tracking=True) is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True) @@ -1564,82 +1577,41 @@ class WorkPieceDelivery(models.Model): production_ids = [] is_cnc_down = 0 is_not_production_line = 0 - is_not_route = 0 same_production_line_id = None same_route_id = None - down_status = '待下发' - production_type = None - num = 0 + production_type = '上产线' + max_num = 4 # 最大配送数量 + if len(self) > max_num: + raise UserError('仅限于配送1-4个制造订单,请重新选择') for item in self: - num += 1 - if production_type is None: - production_type = item.type - if item.type == "运送空料架": - if num >= 2: - raise UserError('仅选择一条路线进行配送,请重新选择') - else: - delivery_ids.append(item.id) - else: - if num > 4: - raise UserError('仅限于配送1-4个制造订单,请重新选择') - if item.status in ['待配送', '已配送']: - raise UserError('请选择状态为【待下发】的制造订单进行配送') - if item.route_id: - if same_route_id is None: - same_route_id = item.route_id.id - if item.route_id.id != same_route_id: - is_not_route += 1 - # else: - # raise UserError('请选择【任务路线】再进行配送') - # if item.production_id.production_line_state == '已下产线' and item.state == '待下发' and item.type == '下产线': - # raise UserError('该制造订单已下产线,无需配送') - if production_type != item.type: - raise UserError('请选择类型为%s的制造订单进行配送' % production_type) - if down_status != item.status: - up_workpiece = self.search([('type', '=', '上产线'), ('production_id', '=', item.production_id), - ('status', '=', '待下发')]) - if up_workpiece: - raise UserError('您所选择的制造订单暂未上产线,请在上产线后再进行配送') - else: - raise UserError('请选择状态为【待下发】的制造订单进行配送') - - if same_production_line_id is None: - same_production_line_id = item.production_line_id.id - if item.production_line_id.id != same_production_line_id: - is_not_production_line += 1 - if item.is_cnc_program_down is False: - is_cnc_down += 1 - if is_cnc_down == 0 and is_not_production_line == 0 and is_not_route == 0: - delivery_ids.append(item.id) - production_ids.append(item.production_id.id) + if item.status != '待下发': + raise UserError('请选择状态为【待下发】的制造订单进行配送') + if same_production_line_id is None: + same_production_line_id = item.production_line_id.id + if item.production_line_id.id != same_production_line_id: + is_not_production_line += 1 + 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) + production_ids.append(item.production_id.id) if is_cnc_down >= 1: raise UserError('您所选择制造订单的【CNC程序】暂未下发,请在程序下发后再进行配送') if is_not_production_line >= 1: raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认') - if is_not_route >= 1: - raise UserError('您所选择制造订单的【任务路线】不一致,请重新确认') - is_free = self._check_avgsite_state() - if is_free is True: - if delivery_ids: - return { - 'name': _('确认'), - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'sf.workpiece.delivery.wizard', - 'target': 'new', - 'context': { - 'default_delivery_ids': [(6, 0, delivery_ids)], - 'default_production_ids': [(6, 0, production_ids)], - 'default_destination_production_line_id': same_production_line_id, - 'default_route_id': same_route_id, - 'default_type': production_type, - }} - else: - if production_type == '运送空料架': - raise UserError("您所选择的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时进行配送") - else: - raise UserError( - "您所选择制造订单的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时或选择其他路线进行配送") + if delivery_ids: + return { + 'name': _('确认'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'sf.workpiece.delivery.wizard', + 'target': 'new', + 'context': { + 'default_delivery_ids': [(6, 0, delivery_ids)], + 'default_production_ids': [(6, 0, production_ids)], + 'default_type': production_type, + }} + # 验证agv站点是否可用 def _check_avgsite_state(self): @@ -1747,6 +1719,9 @@ class WorkPieceDelivery(models.Model): # agv调度单 agv_dispatch_id = fields.Many2one('sf.agv.dispatch', 'agv调度单') + def on_barcode_scanned(self, barcode): + logging.info('Rfid:%s' % barcode) + class CMMprogram(models.Model): _name = 'sf.cmm.program' diff --git a/sf_manufacturing/views/agv_setting_views.xml b/sf_manufacturing/views/agv_setting_views.xml index 66f728f0..62e3c623 100644 --- a/sf_manufacturing/views/agv_setting_views.xml +++ b/sf_manufacturing/views/agv_setting_views.xml @@ -8,7 +8,7 @@ - + @@ -42,7 +42,7 @@ - + diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 805dcb1f..60c0b8a0 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -643,15 +643,15 @@ - + + - + - - - + + + diff --git a/sf_manufacturing/wizard/workpiece_delivery_views.xml b/sf_manufacturing/wizard/workpiece_delivery_views.xml index a8d00d72..96ccfc7f 100644 --- a/sf_manufacturing/wizard/workpiece_delivery_views.xml +++ b/sf_manufacturing/wizard/workpiece_delivery_views.xml @@ -9,23 +9,17 @@ - +
- - + + +
- - - - -
- 是否确定配送 -