修改AGV调度系统

This commit is contained in:
胡尧
2024-08-09 17:32:46 +08:00
parent a79500d0ad
commit 9dbea66b73
14 changed files with 349 additions and 169 deletions

View File

@@ -15,14 +15,14 @@
'data/stock_data.xml',
'data/empty_racks_data.xml',
'data/panel_data.xml',
'data/agv_dispatch_data.xml',
'data/agv_scheduling_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',
'wizard/rework_wizard_views.xml',
'wizard/production_wizard_views.xml',
'views/mrp_views_menus.xml',
'views/agv_dispatch_views.xml',
'views/agv_scheduling_views.xml',
'views/stock_lot_views.xml',
'views/mrp_production_addional_change.xml',
'views/mrp_routing_workcenter_view.xml',

View File

@@ -2,6 +2,8 @@
import logging
import json
from datetime import datetime
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http
from odoo.http import request
@@ -448,6 +450,9 @@ class Manufacturing_Connect(http.Controller):
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
if 'IsComplete' in ret:
first_rfid_code = None
production_ids = []
workpiece_validate = True
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
logging.info('F-RfidCode:%s' % i)
@@ -455,6 +460,7 @@ class Manufacturing_Connect(http.Controller):
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
if rfid_code is not None:
first_rfid_code = first_rfid_code or rfid_code
domain = [
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
@@ -462,6 +468,7 @@ class Manufacturing_Connect(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
production_ids.append(order.production_id.id)
if order.production_line_state == '待上产线':
logging.info(
'工单产线状态:%s' % order.production_line_state)
@@ -470,23 +477,37 @@ class Manufacturing_Connect(http.Controller):
('processing_panel', '=', order.processing_panel)])
if panel_workorder:
panel_workorder.write({'production_line_state': '已上产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
('production_id', '=', order.production_id.id),
('workorder_id', '=', order.id),
('workorder_state', '=', 'done')])
if workpiece_delivery.status == '待下发':
workpiece_delivery.write({'is_manual_work': True})
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
# [
# ('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
# ('production_id', '=', order.production_id.id),
# ('workorder_id', '=', order.id),
# ('workorder_state', '=', 'done')])
# if workpiece_delivery.status == '待下发':
# workpiece_delivery.write({'is_manual_work': True})
# 下发
else:
workpiece_validate = False
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
if ret['IsComplete'] is True:
# 将RFID_CODE对应的工件配送单对应的AGV任务调度状态设置为已配送
request.env['sf.workpiece.delivery'].sudo().search(
[('rfid_code', '=', first_rfid_code)]).agv_scheduling_id.write({'state': '已配送'})
# 向AGV任务调度下发运送空料架任务
productions = request.env['mrp.production'].sudo().search([('id', 'in', production_ids)])
# 获取设备ID对应的接驳站配置
agv_site = request.env['sf.agv.site'].sudo().search(
[('name', '=', ret['DeviceId'])], limit=1)
request.env['sf.agv.scheduling'].add_scheduling(agv_site.id, '运送空料架', productions)
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
else:
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
except RepeatTaskException as e:
logging.info('AGVToProduct error:%s' % e)
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
logging.info('AGVToProduct error:%s' % e)
return json.JSONEncoder().encode(res)
@@ -600,3 +621,27 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/GetAgvStationState', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVStationState(self, **kw):
"""
中控推送接驳站状态
:param kw:
:return:
"""
logging.info('AGVStationState:%s' % kw)
try:
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/AGVStationState'})
logging.info('ret:%s' % ret)
if 'DeviceId' in ret and 'AtHome' in ret:
logging.info('DeviceId:%s, AtHome:%s' % (ret['DeviceId'], ret['AtHome']))
request.env['sf.agv.site'].update_site_state(ret['DeviceId'], ret['AtHome'])
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="sequence_workpiece_delivery" model="ir.sequence">
<data noupdate="0">
<record id="sequence_agv_scheduling" model="ir.sequence">
<field name="name">AGV调度</field>
<field name="code">sf.agv.dispatch</field>
<field name="code">sf.agv.scheduling</field>
<field name="prefix">B%(year)s%(month)s%(day)s</field>
<field name="padding">4</field>
<field name="company_id" eval="False"/>

View File

@@ -9,4 +9,4 @@ from . import stock
from . import res_user
from . import production_line_base
from . import agv_setting
from . import agv_dispatch
from . import agv_scheduling

View File

@@ -1,76 +0,0 @@
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('任务单号', 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='终点接驳站状态')
state = fields.Selection([
('待下发', '待下发'),
('配送中', '配送中'),
('已配送', '已配送'),
('已取消', '已取消')], 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')
@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 = ''
@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

View File

@@ -0,0 +1,191 @@
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)
workpiece_delivery_ids = fields.One2many('sf.workpiece.delivery', 'agv_scheduling_id', 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:
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 = '已取消'

View File

@@ -18,39 +18,53 @@ class AgvSetting(models.Model):
divide_the_work = fields.Char('主要分工')
active = fields.Boolean('有效', default=True)
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True,
domain=[('is_agv_dispatch', '=', True)])
domain=[('is_agv_scheduling', '=', True)])
def update_site_state(self):
# 调取中控的接驳站接口并修改对应站点的状态
config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
headers = {'Authorization': config['center_control_Authorization']}
center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
timestamp = int(time.time())
center_control_url += str(timestamp)
logging.info('工件配送-请求中控地址:%s' % center_control_url)
try:
center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
ret = center_control_r.json()
logging.info('工件配送-请求中控站点信息:%s' % ret)
self.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
if ret['Succeed'] is True:
datas = ret['Datas']
for item in self:
for da in datas:
if da['DeviceId'] == item.name:
if da['AtHome'] is True:
item.state = '占用'
else:
item.state = '空闲'
return True
except requests.exceptions.Timeout:
logging.error('工件配送-请求中控接口超时')
return False
except requests.exceptions.RequestException as e:
logging.error('工件配送-请求中控接口错误: %s', e)
return False
# name必须唯一
_sql_constraints = [
('name_uniq', 'unique (name)', '站点编号必须唯一!'),
]
# def update_site_state(self):
# # 调取中控的接驳站接口并修改对应站点的状态
# config = self.env['res.config.settings'].get_values()
# # token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
# headers = {'Authorization': config['center_control_Authorization']}
# center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
# timestamp = int(time.time())
# center_control_url += str(timestamp)
# logging.info('工件配送-请求中控地址:%s' % center_control_url)
# try:
# center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
# ret = center_control_r.json()
# logging.info('工件配送-请求中控站点信息:%s' % ret)
# self.env['center_control.interface.log'].sudo().create(
# {'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
# if ret['Succeed'] is True:
# datas = ret['Datas']
# for item in self:
# for da in datas:
# if da['DeviceId'] == item.name:
# if da['AtHome'] is True:
# item.state = '占用'
# else:
# item.state = '空闲'
# return True
# except requests.exceptions.Timeout:
# logging.error('工件配送-请求中控接口超时')
# return False
# except requests.exceptions.RequestException as e:
# logging.error('工件配送-请求中控接口错误: %s', e)
# return False
def update_site_state(self, agv_site_name, is_occupy):
if agv_site_name:
agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
if agv_site:
agv_site.state = '占用' if is_occupy else '空闲'
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
else:
raise UserError("更新失败:接驳站站点错误!")
class AgvTaskRoute(models.Model):
@@ -73,7 +87,7 @@ class AgvTaskRoute(models.Model):
if self.end_site_id == self.start_site_id:
raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择")
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_dispatch', '=', True)],
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_scheduling', '=', True)],
compute="_compute_region")
@api.depends('end_site_id')

View File

@@ -125,7 +125,7 @@ class ResWorkcenter(models.Model):
return res
# AGV是否可配送
is_agv_dispatch = fields.Boolean(string="AGV配送", tracking=True)
is_agv_scheduling = fields.Boolean(string="AGV所属区域", tracking=True)
class ResWorkcenterProductivity(models.Model):
_inherit = 'mrp.workcenter.productivity'

View File

@@ -1717,7 +1717,7 @@ class WorkPieceDelivery(models.Model):
obj.delivery_duration = 0.0
# agv调度单
agv_dispatch_id = fields.Many2one('sf.agv.dispatch', 'agv调度单')
agv_scheduling_id = fields.Many2one('sf.agv.scheduling', 'agv调度单')
def on_barcode_scanned(self, barcode):
logging.info('Rfid:%s' % barcode)

View File

@@ -150,7 +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
access_sf_agv_scheduling_admin,sf_agv_scheduling_admin,model_sf_agv_scheduling,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
150
151
152
153
154
155
156

View File

@@ -2,9 +2,9 @@
<odoo>
<data>
<!-- agv站点 -->
<record id="view_agv_dispatch_tree" model="ir.ui.view">
<record id="view_agv_scheduling_tree" model="ir.ui.view">
<field name="name">agv调度</field>
<field name="model">sf.agv.dispatch</field>
<field name="model">sf.agv.scheduling</field>
<field name="arch" type="xml">
<tree editable="bottom" delete="0" create="0">
<field name="state" widget="badge"
@@ -18,33 +18,35 @@
<field name="start_site_id"/>
<field name="end_site_id"/>
<field name="site_state"/>
<field name="delivery_workpieces" readonly="1"/>
<field name="delivery_workpieces_display"/>
<field name="task_create_time" readonly="1"/>
<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', '!=', '待下发')]}"
icon="fa-times"
class="btn-danger"
confirm="你确定要取消这条记录吗?"
/>
</tree>
</field>
</record>
<record id="action_agv_dispatch_tree" model="ir.actions.act_window">
<record id="action_agv_scheduling_tree" model="ir.actions.act_window">
<field name="name">AGV调度</field>
<field name="res_model">sf.agv.dispatch</field>
<field name="res_model">sf.agv.scheduling</field>
<field name="view_mode">tree</field>
</record>
<!-- <menuitem id="menu_action_agv_dispatch"-->
<!-- parent="mrp.menu_mrp_manufacturing"-->
<!-- name="AGV调度"-->
<!-- action="sf_manufacturing.action_agv_dispatch_tree"-->
<!-- groups="sf_base.group_sf_order_user,sf_base.group_plan_dispatch,sf_base.group_plan_director,sf_base.group_sf_mrp_manager"-->
<!-- />-->
<menuitem
id="sf_workpiece_delivery_menu1"
name="AGV调度"
sequence="28"
action="action_agv_dispatch_tree"
parent="mrp.menu_mrp_manufacturing"
/>
<menuitem
id="menu_action_agv_scheduling"
name="AGV调度"
sequence="28"
action="action_agv_scheduling_tree"
parent="mrp.menu_mrp_manufacturing"
/>
</data>
</odoo>

View File

@@ -182,7 +182,7 @@
</xpath>
<xpath expr="//field[@name='resource_calendar_id']" position="after">
<field name="is_process_outsourcing"/>
<field name="is_agv_dispatch"/>
<field name="is_agv_scheduling"/>
</xpath>
</field>
</record>

View File

@@ -17,7 +17,7 @@
<button name="recognize_production" string="识别" type="object" class="oe_highlight"/>
</div>
<field name="type" readonly="1"/>
<field name="feeder_station_start_id" options="{'no_create': True}" require="1"/>
<field name="feeder_station_start_id" options="{'no_create': True}" required="1"/>
<field name="workcenter_id" options="{'no_create': True}"/>
</group>
<footer>

View File

@@ -50,29 +50,33 @@ class WorkpieceDeliveryWizard(models.TransientModel):
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
def confirm(self):
if self.workorder_id:
self.workorder_id.workpiece_delivery_ids[0].agv_dispatch_id()
else:
is_not_production_line = 0
same_production_line_id = None
notsame_production_line_arr = []
for item in self.production_ids:
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:
notsame_production_line_arr.append(item.name)
notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
if is_not_production_line >= 1:
raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
try:
if self.workorder_id:
self.workorder_id.workpiece_delivery_ids[0].agv_scheduling_id()
else:
# self.delivery_ids._delivery_avg()
agv_dispatch_id = self.env['sf.agv.dispatch'].add_queue(
agv_start_site_id=self.feeder_station_start_id.id,
agv_route_type=self.type,
production_ids=self.production_ids,
delivery_ids=self.delivery_ids.ids
)
self.delivery_ids.agv_dispatch_id = agv_dispatch_id
is_not_production_line = 0
same_production_line_id = None
notsame_production_line_arr = []
for item in self.production_ids:
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:
notsame_production_line_arr.append(item.name)
notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
if is_not_production_line >= 1:
raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
else:
# self.delivery_ids._delivery_avg()
agv_scheduling_id = self.env['sf.agv.scheduling'].add_scheduling(
agv_start_site_id=self.feeder_station_start_id.id,
agv_route_type=self.type,
productions=self.production_ids,
deliveries=self.delivery_ids
)
self.delivery_ids.agv_scheduling_id = agv_scheduling_id
except Exception as e:
logging.info('工件配送任务下发失败:%s' % e)
raise UserError('工件配送任务下发失败:%s' % e)
def recognize_production(self):
# production_ids = []