修改AGV调度系统
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
191
sf_manufacturing/models/agv_scheduling.py
Normal file
191
sf_manufacturing/models/agv_scheduling.py
Normal 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 = '已取消'
|
||||
@@ -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')
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user