diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 6a122097..1c49c88b 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -24,6 +24,7 @@ 'views/mrp_workcenter_views.xml', 'views/mrp_workorder_view.xml', 'views/model_type_view.xml', + 'views/agv_setting_views.xml', 'views/sf_maintenance_equipment.xml', ], diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index 2cb6c2df..28f0702c 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -431,17 +431,17 @@ class Manufacturing_Connect(http.Controller): if 'DeviceId' in ret: logging.info('DeviceId:%s' % ret['DeviceId']) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( - [('feeder_station_destination', '=', ret['DeviceId'])]) + [('feeder_station_start_id.name', '=', ret['DeviceId']), + ('status', '=', '已配送'), ('type', '=', '上产线')], order='id asc') if workpiece_delivery: for wd in workpiece_delivery: - logging.info('wd.workorder_id:%s' % wd.workorder_id.name) + logging.info('wd.production_id:%s' % wd.production_id.name) if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线': - logging.info('wd.production_id:%s' % wd.production_id.name) logging.info('wd.production_line_state:%s' % wd.production_id.production_line_state) wd.production_id.write({'production_line_state': '已上产线'}) wd.write({'production_line_state': '已上产线'}) else: - res = {'Succeed': False, 'ErrorCode': 203, 'Error': '该DeviceId没有对应的工件配送数据'} + res = {'Succeed': False, 'ErrorCode': 203, 'Error': '该DeviceId没有对应的已配送工件数据'} else: res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'} except Exception as e: @@ -466,26 +466,16 @@ class Manufacturing_Connect(http.Controller): if 'DeviceId' in ret: logging.info('DeviceId:%s' % ret['DeviceId']) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( - [('feeder_station_destination', '=', ret['DeviceId'])]) + [('feeder_station_start_id.name', '=', ret['DeviceId']), + ('status', '=', '已配送'), ('type', '=', '下产线')], order='id asc') if workpiece_delivery: for wd in workpiece_delivery: - logging.info('wd.workorder_id:%s' % wd.workorder_id.name) + logging.info('wd.production_id:%s' % wd.production_id.name) if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '已上产线': - logging.info('wd.production_id:%s' % wd.production_id.name) logging.info('wd.production_line_state:%s' % wd.production_id.production_line_state) - workpiece_delivery_off=request.env['sf.workpiece.delivery'].create({ - 'production_id': wd.production_id.id, - 'feeder_station_start': ret['DeviceId'], - 'feeder_station_destination': '', - 'workorder_id': workpiece_delivery.workorder_id, - 'workpiece_code': workpiece_delivery.workpiece_code, - 'production_line_id': workpiece_delivery.production_line_id, - 'task_delivery_time': datetime.now(), - 'production_line_state': '已下产线' - }) wd.production_id.write({'production_line_state': '已下产线'}) logging.info('开始向agv下发下产线任务') - workpiece_delivery_off._delivery_avg() + wd._delivery_avg() logging.info('agv下发下产线任务已配送') else: diff --git a/sf_manufacturing/controllers/workpiece.py b/sf_manufacturing/controllers/workpiece.py index e2f60421..775da5a0 100644 --- a/sf_manufacturing/controllers/workpiece.py +++ b/sf_manufacturing/controllers/workpiece.py @@ -25,9 +25,14 @@ class Workpiece(http.Controller): if 'method' in ret: if ret['method'] == 'end': workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( - [('production_id.name', '=', ret['reqCode'])]) + [('production_id.name', '=', ret['reqCode']), ('agv_task_code'), '=', ret['taskCode']]) if workpiece_delivery: workpiece_delivery.write({'status': '已配送', 'task_completion_time': ret['reqTime']}) + if workpiece_delivery.type == '下产线': + workpiece_delivery_clear = request.env['sf.workpiece.delivery'].sudo().search( + [('production_id.name', '=', ret['reqCode']), ('type', '=', '运送空料架')]) + if workpiece_delivery_clear: + workpiece_delivery_clear._delivery_avg() else: res = {'Succeed': False, 'ErrorCode': 203, 'Error': '该reqCode暂未查到对应的工件配送记录'} else: diff --git a/sf_manufacturing/models/__init__.py b/sf_manufacturing/models/__init__.py index 3ca83b27..3de23ef3 100644 --- a/sf_manufacturing/models/__init__.py +++ b/sf_manufacturing/models/__init__.py @@ -8,3 +8,4 @@ from . import mrp_routing_workcenter from . import stock from . import res_user from . import production_line_base +from . import agv_setting diff --git a/sf_manufacturing/models/agv_setting.py b/sf_manufacturing/models/agv_setting.py new file mode 100644 index 00000000..70075285 --- /dev/null +++ b/sf_manufacturing/models/agv_setting.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +import requests +import logging +from odoo import fields, models + + +class AgvSetting(models.Model): + _name = 'sf.agv.site' + _description = 'agv站点' + + number = fields.Integer('序号') + name = fields.Char('位置编号') + owning_region = fields.Char('所属区域') + state = fields.Selection([ + ('占用', '占用'), + ('空闲', '空闲')], string='状态') + divide_the_work = fields.Char('主要分工') + active = fields.Boolean('有效', default=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" + center_control_r = requests.get(center_control_url, params={}, headers=headers) + ret = center_control_r.json() + logging.info('工件配送-请求中控站点信息:%s' % ret) + datas = ret['Datas'] + for item in self: + for da in datas: + if da['DeviceId'] == item.name: + if da['AtHome'] is True: + item.state = '占用' + + +class AgvTaskRoute(models.Model): + _name = 'sf.agv.task.route' + _description = 'agv任务路线' + + name = fields.Char('名称') + type = fields.Selection([ + ('F01', '搬运'), ], string='类型', default="F01") + start_site_id = fields.Many2one('sf.agv.site', '起点接驳站位置编号') + end_site_id = fields.Many2one('sf.agv.site', '终点接驳站位置编号') + destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线') + priority = fields.Selection([ + ('0', '正常'), + ('1', '低'), + ('2', '中'), + ('3', '高'), + ('4', '紧急'), + ], string='优先级', default='0') + active = fields.Boolean('有效', default=True) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 04b4d913..4b915392 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -135,7 +135,8 @@ class MrpProduction(models.Model): # cnc程序获取 def fetchCNC(self): cnc = self.env['mrp.production'].search([('id', '=', self.id)]) - quick_order = self.env['quick.easy.order'].search([('id', '=', cnc.product_id.id)]) + quick_order = self.env['quick.easy.order'].search( + [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])]) programme_way = False if cnc.manual_quotation is True: programme_way = 'manual operation' @@ -157,7 +158,6 @@ class MrpProduction(models.Model): 'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height, 'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width, 'order_no': cnc.origin, - 'quick_order_no': quick_order.name if quick_order else False, 'model_order_no': cnc.product_id.default_code, 'user': cnc.env.user.name, 'programme_way': programme_way, diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index c7b49ab7..c0a0d218 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -106,7 +106,7 @@ class ResMrpWorkOrder(models.Model): cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序") cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序") tray_code = fields.Char(string="托盘编码") - glb_file = fields.Binary("glb模型文件") + glb_file = fields.Binary("glb模型文件", related='production_id.model_file') is_subcontract = fields.Boolean(string='是否外协') surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数") picking_in_id = fields.Many2one('stock.picking', string='外协入库单') @@ -397,8 +397,8 @@ class ResMrpWorkOrder(models.Model): def button_workpiece_delivery(self): if self.routing_type == '装夹预调': for item in self.workpiece_delivery_ids: - if not item.feeder_station_start: - raise UserError('【工件配送】明细中请输入起点接驳站') + if not item.route_id: + raise UserError('【工件配送】明细中请选择【任务路线】') # if not item.workpiece_code: # raise UserError('请对【同运工件】进行扫描') else: @@ -458,12 +458,18 @@ class ResMrpWorkOrder(models.Model): 'date_planned_finished': False, 'duration_expected': duration_expected, 'duration': 0, - 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self.env[ - 'sf.workpiece.delivery'].create( - {'production_id': production.id}) + '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): + return [ + [0, '', {'production_id': production.id, 'type': '上产线'}], + [0, '', {'production_id': production.id, 'type': '下产线'}], + [0, '', {'production_id': production.id, 'type': '运送空料架'}], + ] + # 拼接工单对象属性值(表面工艺) def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id): workorders_values_str = [0, '', { @@ -652,47 +658,6 @@ class ResMrpWorkOrder(models.Model): else: self.results = '合格' - # cnc程序获取 - def fetchCNC(self): - try: - cnc = self.env['mrp.workorder'].search( - [('routing_type', '=', 'CNC加工'), ('production_id', '=', self.production_id.id)], limit=1) - res = {'model_code': '' if not cnc.product_id.model_code else cnc.product_id.model_code, - 'production_no': self.production_id.name, - 'machine_tool_code': cnc.workcenter_id.equipment_id.code, - 'material_code': cnc.env['sf.production.materials'].search( - [('id', '=', cnc.product_id.materials_id.id)]).materials_no, - 'material_type_code': cnc.env['sf.materials.model'].search( - [('id', '=', cnc.product_id.materials_type_id.id)]).materials_no, - 'machining_processing_panel': cnc.product_id.model_processing_panel, - 'machining_precision': cnc.product_id.model_machining_precision, - 'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length, - 'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height, - 'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width, - 'order_no': cnc.production_id.origin, - 'model_order_no': cnc.product_id.default_code.rsplit(' -', 1)[0], - 'user': self.env.user.name, - 'model_file': '' if not cnc.product_id.model_file else base64.b64encode( - cnc.product_id.model_file).decode('utf-8') - } - logging.info('res:%s' % res) - configsettings = self.env['res.config.settings'].get_values() - config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) - url = '/api/intelligent_programming/create' - config_url = configsettings['sf_url'] + url - # res_str = json.dumps(res) - ret = requests.post(config_url, json={}, data=res, headers=config_header) - ret = ret.json() - logging.info('fetchCNC-ret:%s' % ret) - if ret['status'] == 1: - self.write( - {'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'}) - else: - raise UserError(ret['message']) - except Exception as e: - logging.info('fetchCNC error:%s' % e) - raise UserError("cnc程序获取编程单失败,请联系管理员") - def json_workorder_str1(self, k, production, route): workorders_values_str = [0, '', { 'product_uom_id': production.product_uom_id.id, @@ -724,6 +689,12 @@ class ResMrpWorkOrder(models.Model): picking_out.write({'state': 'assigned'}) if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress': self.move_raw_ids = self.production_id.move_raw_ids + self.move_raw_ids[0].write({ + 'materiel_length': self.move_raw_ids[0].product_id.length, + 'materiel_width': self.move_raw_ids[0].product_id.width, + 'materiel_height': self.move_raw_ids[0].product_id.height + }) + self.ensure_one() if any(not time.date_end for time in self.time_ids.filtered(lambda t: t.user_id.id == self.env.user.id)): return True @@ -937,7 +908,7 @@ class CNCprocessing(models.Model): # 将FTP的nc文件下载到临时目录 def download_file_tmp(self, production_no, processing_panel): remotepath = os.path.join('/NC', production_no, 'return', processing_panel) - serverdir = os.pat.join('/tmp', production_no, 'return', processing_panel) + serverdir = os.path.join('/tmp', production_no, 'return', processing_panel) ftp_resconfig = self.env['res.config.settings'].get_values() ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'], ftp_resconfig['ftp_password']) @@ -991,7 +962,7 @@ class SfWorkOrderBarcodes(models.Model): # [('routing_type', '=', '装夹预调'), ('production_id', '=', self.production_id.id)]) workorder_old = self.env['mrp.workorder'].search([('rfid_code', '=', barcode)]) if workorder_old: - raise UserError('该托盘已绑定工件,请先解除绑定!!!') + raise UserError('该托盘已绑定【%s】制造订单,请先解除绑定!!!' % workorder_old.production_id.name) if workorder: if workorder.routing_type == '装夹预调': if workorder.state in ['done']: @@ -1114,10 +1085,13 @@ class WorkPieceDelivery(models.Model): store=True) plan_start_processing_time = fields.Datetime('计划开始加工时间', readonly=True) workpiece_code = fields.Char('同运工件编码') - feeder_station_start = fields.Char('起点接驳站') - feeder_station_destination = fields.Char('目的接驳站') + route_id = fields.Many2one('sf.agv.task.route', '任务路线') + feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站') + feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站') task_delivery_time = fields.Datetime('任务下发时间') task_completion_time = fields.Datetime('任务完成时间') + type = fields.Selection( + [('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型') delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration') status = fields.Selection( [('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', @@ -1127,41 +1101,52 @@ class WorkPieceDelivery(models.Model): string='上/下产线', default='待上产线') cnc_program_down_state = fields.Selection([('待下发', '待下发'), ('已下发', '已下发')], string='CNC程序下发状态', default='待下发') + agv_task_code = fields.Char('agv任务单号') + + @api.onchange('route_id') + def onchage_route(self): + if self.route_id: + self.feeder_station_start_id = self.route_id.start_site_id + self.feeder_station_destination_id = self.route_id.end_site_id # 工件配送 def button_delivery(self): if self.cnc_program_down_state == '待下发': - if self.status == '待下发': - return { - 'name': _('确认'), - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'sf.workpiece.delivery.wizard', - 'target': 'new', - 'context': { - 'default_delivery_id': self.id, - }} + if self.route_id: + if self.status == '待下发': + return { + 'name': _('确认'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'sf.workpiece.delivery.wizard', + 'target': 'new', + 'context': { + 'default_delivery_id': self.id, + }} + else: + raise UserError('状态为【待下发】的工件记录可进行配送') else: - raise UserError('状态为【待下发】的工件记录可进行配送') + raise UserError('请选择任务路线再进行配送') else: raise UserError(_("该制造订单还未下发CNC程序单,无法进行工件配送")) # 配送至avg小车 def _delivery_avg(self): - config = self.env['res.config.settings'].get_values() - site_production_line = False - if self.production_line_state == '待上产线': - site_production_line = [('production_line_status', '=', '已上产线')] - elif self.production_line_state == '已下产线': - site_production_line = [('production_line_status', '=', '已下产线')] - agv_site = self.env['res.agv.site'].search(site_production_line, order="number asc") - positionCode_Arr = [] + agv_site = self.env['sf.agv.site'].search([]) if agv_site: - for item in agv_site: - positionCode_Arr.append({ - 'positionCode': item.content, - 'code': item.type - }) + agv_site.update_site_state() + config = self.env['res.config.settings'].get_values() + positionCode_Arr = [] + if self.feeder_station_start_id: + positionCode_Arr.append({ + 'positionCode': self.feeder_station_start_id.name, + 'code': '00' + }) + if self.feeder_station_destination_id: + positionCode_Arr.append({ + 'positionCode': self.feeder_station_destination_id.name, + 'code': '00' + }) res = {'reqCode': self.production_id.name, 'reqTime': '', 'clientCode': '', 'tokenCode': '', 'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'], 'positionCodePath': positionCode_Arr, @@ -1169,17 +1154,17 @@ class WorkPieceDelivery(models.Model): 'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '', 'data': ''} try: - config['agv_rcs_url'] = 'http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask' + # config['agv_rcs_url'] = 'http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask' 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: if self.production_id.name == ret['reqCode']: - self.write({'task_delivery_time': fields.Datetime.now(), 'status': '待配送'}) + self.write( + {'task_delivery_time': fields.Datetime.now(), 'status': '待配送', 'agv_task_code': ret['data']}) else: raise UserError(ret['message']) except Exception as e: diff --git a/sf_manufacturing/security/img.png b/sf_manufacturing/security/img.png new file mode 100644 index 00000000..130affd5 Binary files /dev/null and b/sf_manufacturing/security/img.png differ diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index 8272af30..e8c2449c 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -129,4 +129,7 @@ access_sf_cmm_program_group_plan_dispatch,sf_cmm_program_group_plan_dispatch,mod access_mrp_workcenter_productivity,mrp.workcenter.productivity,mrp.model_mrp_workcenter_productivity,sf_base.group_plan_dispatch,1,0,0,0 access_maintenance_equipment_tool_group_plan_dispatch,maintenance.equipment.tool,sf_manufacturing.model_maintenance_equipment_tool,sf_base.group_plan_dispatch,1,0,0,0 -access_sf_workpiece_delivery_group_plan_dispatch,sf.workpiece.delivery,sf_manufacturing.model_sf_workpiece_delivery,sf_base.group_plan_dispatch,1,0,0,0 \ No newline at end of file +access_sf_workpiece_delivery_group_plan_dispatch,sf.workpiece.delivery,sf_manufacturing.model_sf_workpiece_delivery,sf_base.group_plan_dispatch,1,0,0,0 + +access_sf_agv_site_group_sf_order_user,sf_agv_site_group_sf_order_user,model_sf_agv_site,sf_base.group_sf_order_user,1,1,1,0 +access_sf_agv_task_route_group_sf_order_user,sf_agv_task_route_group_sf_order_user,model_sf_agv_task_route,sf_base.group_sf_order_user,1,1,1,0 \ No newline at end of file diff --git a/sf_manufacturing/views/agv_setting_views.xml b/sf_manufacturing/views/agv_setting_views.xml new file mode 100644 index 00000000..dea85e6a --- /dev/null +++ b/sf_manufacturing/views/agv_setting_views.xml @@ -0,0 +1,59 @@ + + + + + + agv站点 + sf.agv.site + + + + + + + + + + + + + AGV站点 + sf.agv.site + tree + + + + + + + AGV任务路线 + sf.agv.task.route + + + + + + + + + + + + + + AGV任务路线 + sf.agv.task.route + tree + + + + + \ No newline at end of file diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 3ae8bed0..0cc541e7 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -109,16 +109,18 @@