from odoo import models, fields, api, _ from odoo.exceptions import UserError import logging _logger = logging.getLogger(__name__) class RepeatTaskException(UserError): pass class AgvScheduling(models.Model): _name = 'sf.agv.scheduling' _description = 'agv调度' 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='任务类型', required=True) agv_route_name = fields.Char('任务路线名称') 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='终点接驳站状态', default='占用') state = fields.Selection([ ('待下发', '待下发'), ('配送中', '配送中'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态', default='待下发', tracking=True) workorder_ids = fields.One2many('mrp.workorder', string='关联工单') delivery_workpieces = fields.Char('配送工件', required=True, index=True) 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('agv_route_type') def _compute_delivery_workpieces(self): for record in self: record.delivery_workpieces = '、'.join(record.workorder_ids.mapped('production_id.name')) if record.agv_route_type == '运送空料架': record.delivery_workpieces_display = '/' else: record.delivery_workpieces_display = record.delivery_workpieces delivery_workpieces_display = fields.Char('配送工件', compute=_compute_delivery_workpieces) @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 = '' @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.scheduling') or _('New') return super().create(vals_list) def add_scheduling(self, agv_start_site_id, agv_route_type, productions, deliveries=None): """ add_scheduling(agv_start_site_id, agv_route_type, productions, deliveries) -> agv_scheduling 新增AGV调度 params: agv_start_site_id: AGV起点接驳站ID agv_route_type: AGV任务类型 productions: 制造订单 deliveries: 工件配送单 """ # 如果存在配送工件完全相同的AGV调度任务,则不新增 if self.sudo().search([ ('delivery_workpieces', '=', '、'.join(productions.mapped('name'))), ('agv_route_type', '=', agv_route_type), ('state', 'in', ['待下发', '配送中']) ], limit=1): raise RepeatTaskException('已存在相同的AGV调度任务,请勿重复下发!') vals = { 'start_site_id': agv_start_site_id, 'agv_route_type': agv_route_type, 'delivery_workpieces': '、'.join(productions.mapped('name')), 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [], 'task_create_time': fields.Datetime.now() } # 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称 agv_routes = self.env['sf.agv.task.route'].sudo().search([ ('route_type', '=', agv_route_type), ('start_site_id', '=', agv_start_site_id) ]) if len(agv_routes) == 1: vals.update({'end_site_id': agv_routes[0].end_site_id.id, 'agv_route_name': agv_routes[0].name}) try: dispatch = self.env['sf.agv.scheduling'].sudo().create(vals) except Exception as e: _logger.error('添加AGV调度任务失败: %s', e) raise UserError(_('添加AGV调度任务失败: %s', e)) return dispatch def on_site_state_change(self, agv_site_id, agv_site_state): agv_schedulings = self.env['sf.agv.scheduling'].sudo().search([('state', '=', '待下发')], order='id asc') for scheduling in agv_schedulings: if scheduling.end_site_id.id == agv_site_id and scheduling.site_state == '空闲': # 下发AGV调度任务并修改接驳站状态为占用 # self._delivery_avg() # 修改AGV调度任务信息 scheduling.state = '配送中' scheduling.task_delivery_time = fields.Datetime.now() scheduling.site_state = agv_site_state # 更新接驳站状态 self.env['sf.agv.site'].update_site_state(scheduling.end_site_id.name, True) def _delivery_avg(self): config = self.env['res.config.settings'].get_values() positionCode_Arr = [] delivery_Arr = [] feeder_station_start = None feeder_station_destination = None route_id = None for item in self: if route_id is None: route_id = item.route_id.id if feeder_station_start is None: feeder_station_start = item.feeder_station_start_id if feeder_station_destination is None: feeder_station_destination = item.feeder_station_destination_id if item.type in ['上产线', '下产线']: item.route_id = route_id item.feeder_station_start_id = feeder_station_start.id item.feeder_station_destination_id = feeder_station_destination.id delivery_Arr.append(item.name) else: self = self.create( {'name': self.env['ir.sequence'].next_by_code('sf.workpiece.delivery'), 'route_id': self.route_id.id, 'feeder_station_start_id': self.feeder_station_start_id.id, 'feeder_station_destination_id': self.feeder_station_destination_id.id}) delivery_Arr.append(self.name) delivery_str = ','.join(map(str, delivery_Arr)) if feeder_station_start is not None: positionCode_Arr.append({ 'positionCode': feeder_station_start.name, 'code': '00' }) if feeder_station_destination is not None: positionCode_Arr.append({ 'positionCode': feeder_station_destination.name, 'code': '00' }) res = {'reqCode': delivery_str, 'reqTime': '', 'clientCode': '', 'tokenCode': '', 'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'], 'positionCodePath': positionCode_Arr, 'podCode': '', 'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '', 'data': ''} try: logging.info('AGV请求路径:%s' % config['agv_rcs_url']) logging.info('AGV-json:%s' % res) headers = {'Content-Type': 'application/json'} ret = requests.post((config['agv_rcs_url']), json=res, headers=headers) ret = ret.json() logging.info('config-ret:%s' % ret) if ret['code'] == 0: req_codes = ret['reqCode'].split(',') for delivery_item in self: for req_code in req_codes: if delivery_item.name == req_code.strip(): logging.info('delivery_item-name:%s' % delivery_item.name) delivery_item.write({ 'task_delivery_time': fields.Datetime.now(), 'status': '待配送' }) if delivery_item.type == "上产线": delivery_item.workorder_id.write({'is_delivery': True}) else: raise UserError(ret['message']) except Exception as e: logging.info('config-e:%s' % e) raise UserError("工件配送请求agv失败:%s" % e) def button_cancel(self): # 弹出二次确认窗口后执行 for rec in self: if rec.state != '待下发': raise UserError('只有待下发状态的AGV调度任务才能取消!') rec.state = '已取消'