diff --git a/sf_machine_connect/models/__init__.py b/sf_machine_connect/models/__init__.py index ef61fd83..01ae60ae 100644 --- a/sf_machine_connect/models/__init__.py +++ b/sf_machine_connect/models/__init__.py @@ -2,3 +2,4 @@ from . import ftp_client from . import ftp_operate from . import py2opcua from . import res_config_setting +from . import mrp_workorder diff --git a/sf_machine_connect/models/mrp_workorder.py b/sf_machine_connect/models/mrp_workorder.py new file mode 100644 index 00000000..21cc96ac --- /dev/null +++ b/sf_machine_connect/models/mrp_workorder.py @@ -0,0 +1,38 @@ +import re + +from odoo import fields, models, api + + +class ResMrpWorkOrder(models.Model): + _inherit = 'mrp.workorder' + + mixed_search_field = fields.Char(string='坯料产品名称/RFID') + + @api.model + def web_read_group(self, domain, fields, groupby, limit=None, offset=0, orderby=False, + lazy=True, expand=False, expand_limit=None, expand_orderby=False): + domain = domain or [] + for index, item in enumerate(domain): + if isinstance(item, list): + if item[0] == 'mixed_search_field': + if self._is_rfid_code(item[2]): + domain[index] = ['rfid_code', item[1], item[2]] + else: + domain[index] = ['product_tmpl_name', item[1], item[2]] + + return super(ResMrpWorkOrder, self).web_read_group(domain, fields, groupby, limit=limit, offset=offset, orderby=orderby, + lazy=lazy, expand=expand, expand_limit=expand_limit, expand_orderby=expand_orderby) + + def _is_rfid_code(self, tag): + """ + 判断是否是rfid_code + """ + # 基于长度判断(假设RFID标签长度为10到16个字符) + if not 10 <= len(tag) <= 16: + return False + + # 基于字符集判断(仅包含数字和字母) + if not re.match("^[0-9]*$", tag): + return False + + return True \ No newline at end of file diff --git a/sf_machine_connect/views/WorkCenterBarcodes.xml b/sf_machine_connect/views/WorkCenterBarcodes.xml index 9e5d3982..97fee70e 100644 --- a/sf_machine_connect/views/WorkCenterBarcodes.xml +++ b/sf_machine_connect/views/WorkCenterBarcodes.xml @@ -26,6 +26,7 @@ + diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 39da4482..a4199939 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -21,6 +21,7 @@ 'wizard/rework_wizard_views.xml', 'wizard/production_wizard_views.xml', 'views/mrp_views_menus.xml', + 'views/agv_dispatch_views.xml', 'views/stock_lot_views.xml', 'views/mrp_production_addional_change.xml', 'views/mrp_routing_workcenter_view.xml', @@ -30,7 +31,6 @@ 'views/model_type_view.xml', 'views/agv_setting_views.xml', 'views/sf_maintenance_equipment.xml', - ], 'assets': { diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index babda164..037730bc 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -429,7 +429,7 @@ class Manufacturing_Connect(http.Controller): logging.info('LocationChange error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") def AGVToProduct(self, **kw): """ diff --git a/sf_manufacturing/models/__init__.py b/sf_manufacturing/models/__init__.py index 3de23ef3..0b212cdb 100644 --- a/sf_manufacturing/models/__init__.py +++ b/sf_manufacturing/models/__init__.py @@ -9,3 +9,4 @@ from . import stock from . import res_user from . import production_line_base from . import agv_setting +from . import agv_dispatch diff --git a/sf_manufacturing/models/agv_dispatch.py b/sf_manufacturing/models/agv_dispatch.py new file mode 100644 index 00000000..f82b3f07 --- /dev/null +++ b/sf_manufacturing/models/agv_dispatch.py @@ -0,0 +1,50 @@ +from odoo import models, fields, api +from odoo.exceptions import ValidationError + + +class AgvDispatch(models.Model): + _name = 'sf.agv.dispatch' + _description = 'agv调度' + + name = fields.Char('任务单号') + + 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_name = fields.Char('任务路线名称') + start_site_id = fields.Many2one('sf.agv.site', '起点接驳站') + end_site_id = fields.Many2one('sf.agv.site', '终点接驳站') + site_state = fields.Selection([ + ('占用', '占用'), + ('空闲', '空闲')], string='终点接驳站状态') + state = fields.Selection([ + ('待下发', '待下发'), + ('配送中', '配送中'), + ('已配送', '已配送'), + ('已取消', '已取消')], string='状态', default='待下发') + workpiece_delivery_ids = fields.One2many('sf.workpiece.delivery', 'agv_dispatch_id', string='工件配送单') + delivery_workpieces = fields.Char('配送工件', compute='_compute_delivery_workpieces') + + @api.depends('workpiece_delivery_ids') + def _compute_delivery_workpieces(self): + for rec in self: + rec.delivery_workpieces = '\\'.join(rec.workpiece_delivery_ids.mapped('name')) + + task_create_time = fields.Datetime('任务创建时间') + task_delivery_time = fields.Datetime('任务下发时间') + task_completion_time = fields.Datetime('任务完成时间') + task_duration = fields.Char('任务时长', compute='_compute_task_duration') + + @api.depends('task_completion_time', 'task_delivery_time') + def _compute_task_duration(self): + for rec in self: + if rec.task_completion_time and rec.task_delivery_time: + rec.task_duration = str(rec.task_completion_time - rec.task_delivery_time) + 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) diff --git a/sf_manufacturing/models/agv_setting.py b/sf_manufacturing/models/agv_setting.py index 06f9edba..833b2659 100644 --- a/sf_manufacturing/models/agv_setting.py +++ b/sf_manufacturing/models/agv_setting.py @@ -11,12 +11,14 @@ class AgvSetting(models.Model): _description = 'agv站点' name = fields.Char('位置编号') - owning_region = fields.Char('所属区域') + # owning_region = fields.Char('所属区域') state = fields.Selection([ ('占用', '占用'), ('空闲', '空闲')], string='状态') divide_the_work = fields.Char('主要分工') active = fields.Boolean('有效', default=True) + region = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True, + domain=[('is_agv_dispatch', '=', True)]) def update_site_state(self): # 调取中控的接驳站接口并修改对应站点的状态 @@ -71,6 +73,15 @@ 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)], + 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 + class Center_controlInterfaceLog(models.Model): _name = 'center_control.interface.log' diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 64cf2d8d..8fb62e7d 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -124,6 +124,8 @@ class ResWorkcenter(models.Model): res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc] return res + # AGV是否可配送 + is_agv_dispatch = fields.Boolean(string="AGV配送", tracking=True) class ResWorkcenterProductivity(models.Model): _inherit = 'mrp.workcenter.productivity' diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 8f045703..060b06cf 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1744,6 +1744,9 @@ class WorkPieceDelivery(models.Model): else: obj.delivery_duration = 0.0 + # agv调度单 + agv_dispatch_id = fields.Many2one('sf.agv.dispatch', 'agv调度单') + class CMMprogram(models.Model): _name = 'sf.cmm.program' diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index 9b5b5e28..2fd16b53 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -150,5 +150,7 @@ access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_orde access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0 access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0 +access_sf_agv_dispatch_admin,sf_agv_dispatch_admin,model_sf_agv_dispatch,base.group_system,1,1,1,1 + diff --git a/sf_manufacturing/views/agv_dispatch_views.xml b/sf_manufacturing/views/agv_dispatch_views.xml new file mode 100644 index 00000000..5cf608b6 --- /dev/null +++ b/sf_manufacturing/views/agv_dispatch_views.xml @@ -0,0 +1,50 @@ + + + + + + agv调度 + sf.agv.dispatch + + + + + + + + + + + + + + + + + + + AGV调度 + sf.agv.dispatch + tree + + + + + + + + + + + \ No newline at end of file diff --git a/sf_manufacturing/views/agv_setting_views.xml b/sf_manufacturing/views/agv_setting_views.xml index 377bee74..66f728f0 100644 --- a/sf_manufacturing/views/agv_setting_views.xml +++ b/sf_manufacturing/views/agv_setting_views.xml @@ -8,7 +8,7 @@ - + @@ -40,8 +40,9 @@ - + + + diff --git a/sf_manufacturing/views/mrp_workcenter_views.xml b/sf_manufacturing/views/mrp_workcenter_views.xml index ad433bf6..61242cba 100644 --- a/sf_manufacturing/views/mrp_workcenter_views.xml +++ b/sf_manufacturing/views/mrp_workcenter_views.xml @@ -182,6 +182,7 @@ +