diff --git a/sf_base/models/tool_base_new.py b/sf_base/models/tool_base_new.py index 474c46f3..7988dc7a 100644 --- a/sf_base/models/tool_base_new.py +++ b/sf_base/models/tool_base_new.py @@ -1,5 +1,9 @@ # -*- coding: utf-8 -*- +import json +import requests from odoo import fields, models, api +from odoo.exceptions import ValidationError +from odoo.addons.sf_base.commons.common import Common class CuttingToolMaterial(models.Model): @@ -249,3 +253,37 @@ class ToolGroups(models.Model): name = fields.Char('名称') equipment_ids = fields.Many2many('maintenance.equipment', 'ref_maintenance_equipment', string='机台号') remark = fields.Char('备注', size=50) + + # ==========机床刀具组接口========== + def _register_tool_groups(self, obj): + create_url = '/AutoDeviceApi/FeedBackOut' + sf_sync_config = self.env['res.config.settings'].get_values() + token = sf_sync_config['token'] + sf_secret_key = sf_sync_config['sf_secret_key'] + headers = Common.get_headers(obj, token, sf_secret_key) + strurl = sf_sync_config['sf_url'] + create_url + device_id = '' + for equipment_id in obj.equipment_ids: + device_id = '%s,%s' % (device_id, equipment_id.name) + val = { + 'DeviceId': device_id, + 'GroupName': obj.name, + } + kw = json.dumps(val, ensure_ascii=False) + r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers) + ret = r.json() + if r == 200: + return "机床刀具组发送成功" + else: + raise ValidationError("机床刀具组发送失败") + + def write(self, vals): + obj = super().write(vals) + self._register_tool_groups(obj) + return obj + + @api.model_create_multi + def create(self, vals_list): + records = super(ToolGroups, self).create(vals_list) + self._register_tool_groups(records) + return records diff --git a/sf_machine_connect/views/default_delivery.xml b/sf_machine_connect/views/default_delivery.xml index 62f1fcdc..b0890981 100644 --- a/sf_machine_connect/views/default_delivery.xml +++ b/sf_machine_connect/views/default_delivery.xml @@ -10,6 +10,7 @@ + diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 1434078e..eabdff95 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -597,6 +597,7 @@ class CNCprocessing(models.Model): cnc_id = fields.Many2one('ir.attachment') sequence_number = fields.Char('序号') program_name = fields.Char('程序名') + functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型') cutting_tool_name = fields.Char('刀具名称') cutting_tool_no = fields.Char('刀号') processing_type = fields.Char('加工类型') diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 42a392e2..90395d18 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -870,21 +870,9 @@ class SfMaintenanceEquipmentTool(models.Model): _description = '机床刀位' equipment_id = fields.Many2one('maintenance.equipment', string='设备') - product_template_id = fields.Many2one('product.template', string='功能刀具名称', - domain="[('categ_type', '=', '刀具')]") - image_1920 = fields.Binary('图片', related='product_template_id.image_1920') - categ_type = fields.Char(string='功能刀具类型') - diameter = fields.Char('直径') - precision = fields.Char('粗\中\精') - tool_code = fields.Char('功能刀具编码') - hilt_name = fields.Char('刀柄名称') - hilt_code = fields.Char('刀柄编码') - life_value_max = fields.Char('最大寿命值') - alarm_value = fields.Char('报警值') - used_value = fields.Char('已使用值') - code = fields.Char('机床刀位号') - name = fields.Char('', compute='_compute_name') + code = fields.Char('机床刀位号') + name = fields.Char('刀位号', compute='_compute_name') @api.depends('code') def _compute_name(self): diff --git a/sf_manufacturing/views/sf_maintenance_equipment.xml b/sf_manufacturing/views/sf_maintenance_equipment.xml index a2d9437d..b398f994 100644 --- a/sf_manufacturing/views/sf_maintenance_equipment.xml +++ b/sf_manufacturing/views/sf_maintenance_equipment.xml @@ -1,7 +1,7 @@ - + 设备增加刀具库位table sf_manufacturing_equipment.form maintenance.equipment @@ -13,17 +13,6 @@ - - - - - - - - - - - diff --git a/sf_quality/models/__init__.py b/sf_quality/models/__init__.py index 79a87b7d..71468786 100644 --- a/sf_quality/models/__init__.py +++ b/sf_quality/models/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import custom_quality +from . import quality diff --git a/sf_quality/models/quality.py b/sf_quality/models/quality.py new file mode 100644 index 00000000..7382f6be --- /dev/null +++ b/sf_quality/models/quality.py @@ -0,0 +1,34 @@ +import json +import requests +from odoo import fields, models, api +from odoo.exceptions import ValidationError +from odoo.addons.sf_base.commons.common import Common + + +class QualityCheck(models.Model): + _inherit = "quality.check" + _description = '零件特采' + + # ==========零件特采接口========== + def _register_tool_groups(self): + create_url = '/AutoDeviceApi/FeedBackOut' + sf_sync_config = self.env['res.config.settings'].get_values() + token = sf_sync_config['token'] + sf_secret_key = sf_sync_config['sf_secret_key'] + headers = Common.get_headers(self, token, sf_secret_key) + strurl = sf_sync_config['sf_url'] + create_url + val = { + 'RfidCode': None, + } + kw = json.dumps(val, ensure_ascii=False) + r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers) + ret = r.json() + if r == 200: + return "零件特采发送成功" + else: + raise ValidationError("零件特采发送失败") + + @api.onchange('quality_state') + def _onchange_quality_state(self): + if self.quality_state in ['pass', 'fail']: + self._register_tool_groups() diff --git a/sf_tool_management/__init__.py b/sf_tool_management/__init__.py index fb05df81..ea8e8e0b 100644 --- a/sf_tool_management/__init__.py +++ b/sf_tool_management/__init__.py @@ -1,6 +1,7 @@ # -*-coding:utf-8-*- from . import models from . import wizard +from . import controllers from odoo import api, SUPERUSER_ID diff --git a/sf_tool_management/__manifest__.py b/sf_tool_management/__manifest__.py index e334c32a..faf3927b 100644 --- a/sf_tool_management/__manifest__.py +++ b/sf_tool_management/__manifest__.py @@ -16,6 +16,7 @@ 'security/ir.model.access.csv', 'wizard/wizard_view.xml', 'views/tool_base_views.xml', + 'views/sf_maintenance_equipment.xml', 'views/menu_view.xml', 'views/tool_material_search.xml', ], diff --git a/sf_tool_management/controllers/__init__.py b/sf_tool_management/controllers/__init__.py new file mode 100644 index 00000000..e046e49f --- /dev/null +++ b/sf_tool_management/controllers/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/sf_tool_management/controllers/controllers.py b/sf_tool_management/controllers/controllers.py new file mode 100644 index 00000000..0e60a57f --- /dev/null +++ b/sf_tool_management/controllers/controllers.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +import logging +import json +import base64 +from odoo import http +from odoo.http import request + + +class Manufacturing_Connect(http.Controller): + + @http.route('/AutoDeviceApi/FeedBackOut', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + cors="*") + def get_equipment_tool_Info(self, **kw): + """ + 机床当前刀库实时信息 + :param kw: + :return: + """ + logging.info('get_equipment_tool_Info:%s' % kw) + try: + datas = request.httprequest.data + ret = json.loads(datas) + ret = json.loads(ret['result']) + logging.info('DeviceId:%s' % ret) + equipment = request.env['maintenance.equipment'].sudo().search([('name', '=', ret['DeviceId'])]) + + res = {'Succeed': True, 'Datas': []} + if equipment: + for item in equipment: + data = [] + for equipment_tool_id in item.product_template_ids: + functional_tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search( + [('code', '=', equipment_tool_id.tool_code)]) + alarm_time = None + if functional_tool_id.functional_tool_status == '报警': + alarm_time = self.env['sf.functional.tool.warning'].sudo().search( + [('code', '=', equipment_tool_id.tool_code)]).alarm_time + equipment_tool = { + 'RfidCode': None, + 'ToolId': equipment_tool_id.code, + 'ToolName': equipment_tool_id.functional_tool_name_id.name, + 'MaxLife': equipment_tool_id.life_value_max, + 'UseLife': equipment_tool_id.used_value, + 'AddDatetime': equipment_tool_id.tool_install_time, + 'State': functional_tool_id.functional_tool_status, + 'WarnDate': alarm_time if alarm_time else False + } + data.append(equipment_tool) + res['Datas'].append({ + 'DeviceId': item.name, + 'Data': data + }) + except Exception as e: + res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} + logging.info('get_equipment_tool_Info error:%s' % e) + return json.JSONEncoder().encode(res) diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 149ccb5a..f5995b86 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- import re +import json +import requests from datetime import timedelta -from odoo import fields, models, api from odoo import SUPERUSER_ID +from odoo import fields, models, api from odoo.exceptions import ValidationError +from odoo.addons.sf_base.commons.common import Common class FunctionalCuttingToolEntity(models.Model): @@ -174,6 +177,34 @@ class FunctionalCuttingToolEntity(models.Model): ('coarse_middle_thin', '=', self.coarse_middle_thin)] return result + # ==========刀具组接口========== + def _register_functional_tool_groups(self, obj): + create_url = '/AutoDeviceApi/FeedBackOut' + sf_sync_config = self.env['res.config.settings'].get_values() + token = sf_sync_config['token'] + sf_secret_key = sf_sync_config['sf_secret_key'] + headers = Common.get_headers(obj, token, sf_secret_key) + strurl = sf_sync_config['sf_url'] + create_url + val = { + 'ToolName': obj.name, + 'GroupName': obj.tool_groups_id.name, + 'ToolId': obj.code + } + kw = json.dumps(val, ensure_ascii=False) + r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers) + ret = r.json() + if r == 200: + return "刀具组发送成功" + else: + raise ValidationError("刀具组发送失败") + + @api.model_create_multi + def create(self, vals): + obj = super(FunctionalCuttingToolEntity, self).create(vals) + # 调用刀具组接口 + self._register_functional_tool_groups(obj) + return obj + class FunctionalToolWarning(models.Model): _name = 'sf.functional.tool.warning' @@ -240,7 +271,7 @@ class FunctionalToolWarning(models.Model): class StockMoveLine(models.Model): _inherit = 'stock.move.line' _description = '功能刀具出入库记录' - # _order = 'install_tool_time desc' + _order = 'date desc' functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称') functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, @@ -594,16 +625,16 @@ class CAMWorkOrderProgramKnifePlan(models.Model): _name = 'sf.cam.work.order.program.knife.plan' _description = 'CAM工单程序用刀计划' - name = fields.Char(string='工单任务编号', readonly=False) - cam_procedure_code = fields.Char(string='CAM程序编号', readonly=False) - cam_cutter_spacing_code = fields.Char(string='CAM刀位号', readonly=False) + name = fields.Char('工单任务编号') + cam_procedure_code = fields.Char('程序名') + filename = fields.Char('文件') + cam_cutter_spacing_code = fields.Char('刀号') tool_position_interface_type = fields.Selection( [('BT刀柄式', 'BT刀柄式'), ('SK刀柄式', 'SK刀柄式'), ('HSK刀柄式', 'HSK刀柄式'), ('CAT刀柄式', 'CAT刀柄式'), ('ISO刀盘式', 'ISO刀盘式'), ('DIN刀盘式', 'DIN刀盘式'), ('直装固定式', '直装固定式')], string='刀位接口型号') - production_line_id = fields.Many2one('sf.production.line', string='生产线', readonly=False, - group_expand='_read_group_names') - machine_table_name_id = fields.Many2one('maintenance.equipment', string='机床名称', readonly=False, + production_line_id = fields.Many2one('sf.production.line', string='生产线', group_expand='_read_group_names') + machine_table_name_id = fields.Many2one('maintenance.equipment', string='机床名称', domain="[('production_line_id', '=', production_line_id)]") machine_table_name = fields.Char(string='机台号', readonly=True, related='machine_table_name_id.name') cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', required=True, @@ -616,8 +647,9 @@ class CAMWorkOrderProgramKnifePlan(models.Model): barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', domain=[('product_id.name', '=', '功能刀具')]) - functional_tool_name = fields.Char(string='功能刀具名称', required=True) + functional_tool_name = fields.Char(string='功能刀具名称', compute='_compute_functional_tool_name') functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False) + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组') diameter = fields.Integer(string='刀具直径(mm)', readonly=False) tool_included_angle = fields.Float(string='刀尖R角(mm)', readonly=False) tool_loading_length = fields.Float(string='总长度(mm)', readonly=False) @@ -629,12 +661,28 @@ class CAMWorkOrderProgramKnifePlan(models.Model): L_D = fields.Float(string='L/D值', readonly=False) clearance_length = fields.Float(string='避空长(mm)', readonly=False) required_cutting_time = fields.Integer(string='需切削时长', readonly=False) + process_type = fields.Char('加工类型') + margin_x_y = fields.Float('余量_X/Y') + margin_z = fields.Float('余量_Z') + finish_depth = fields.Float('加工深度') + shank_model = fields.Char('刀柄型号') + estimated_processing_time = fields.Char('预计加工时间') plan_execute_status = fields.Selection([('0', '待下发'), ('1', '执行中'), ('2', '已完成')], string='计划执行状态', default='0', readonly=False) sf_functional_tool_assembly_id = fields.Many2one('sf.functional.tool.assembly', '功能刀具组装', readonly=True) + @api.depends('diameter', 'tool_included_angle', 'tool_groups_id') + def _compute_functional_tool_name(self): + for obj in self: + if obj.tool_groups_id: + obj.functional_tool_name = '%s-D%sR%s' % ( + obj.tool_groups_id.name, obj.diameter, + obj.tool_included_angle) + else: + obj.functional_tool_name = None + @api.model def _read_group_names(self, categories, domain, order): names = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) @@ -646,27 +694,34 @@ class CAMWorkOrderProgramKnifePlan(models.Model): :return: """ record = self.env['sf.functional.tool.assembly'].create({ - 'barcode_id': self.barcode_id.id, - 'functional_tool_name_id': self.functional_tool_name_id.id, + 'functional_tool_name': self.functional_tool_name, 'functional_tool_type_id': self.functional_tool_type_id.id, + 'tool_groups_id': self.tool_groups_id.id, 'functional_tool_diameter': self.diameter, - 'functional_tool_length': self.tool_loading_length, - 'loading_task_source': '0', - 'coarse_middle_thin': None, - 'tool_loading_length': None, - 'applicant': self.env.user.name, - 'reason_for_applying': self.reason_for_applying, - 'use_tool_time': self.need_knife_time, + 'knife_tip_r_angle': self.tool_included_angle, + 'tool_loading_length': self.tool_loading_length, + 'functional_tool_length': self.extension_length, + 'effective_length': self.effective_length, + 'whether_standard_knife': self.whether_standard_knife, + 'coarse_middle_thin': self.coarse_middle_thin, + 'new_former': self.new_former, + 'production_line_name_id': self.production_line_id.id, 'machine_tool_name_id': self.machine_table_name_id.id, - 'machine_tool_code': self.cam_procedure_code, - 'cutter_spacing_code': self.cam_cutter_spacing_code, + 'cutter_spacing_code_id': self.env['maintenance.equipment.tool'].sudo().search( + [('code', '=', self.cam_cutter_spacing_code), ('equipment_id', '=', self.machine_table_name_id.id)]).id, + + 'loading_task_source': '0', + 'applicant': self.env.user.name, + 'use_tool_time': self.need_knife_time, + 'reason_for_applying': '工单用刀', + 'sf_cam_work_order_program_knife_plan_id': self.id }) self.sf_functional_tool_assembly_id = record.id # 将计划执行状态改为执行中 self.env['sf.cam.work.order.program.knife.plan'].search( - [('barcode_id', '=', self.barcode_id.id)]).write( + [('name', '=', self.name), ('functional_tool_name', '=', self.functional_tool_name)]).write( {'plan_execute_status': '1', 'applicant': self.env.user.name}) @@ -676,16 +731,50 @@ class CAMWorkOrderProgramKnifePlan(models.Model): :return: """ self.env['sf.functional.tool.assembly'].search( - [('barcode_id', '=', self.barcode_id.id), + [('assembly_order_code', '=', self.sf_functional_tool_assembly_id.assembly_order_code), ('loading_task_source', '=', '0')]).unlink() # 将计划执行状态改为待执行,同时清除申请人、功能刀具组装字段数据 self.env['sf.cam.work.order.program.knife.plan'].search( - [('barcode_id', '=', self.barcode_id.id)]).write( + [('name', '=', self.name), ('functional_tool_name', '=', self.functional_tool_name)]).write( {'plan_execute_status': '0', 'applicant': None, 'sf_functional_tool_assembly_id': None}) + def create_cam_work_plan(self, cnc_processing): + """ + 根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划 + """ + status = False + if cnc_processing.functional_tool_type_id and cnc_processing.cutting_tool_name: + functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search( + [('sf_cutting_tool_type_id', '=', cnc_processing.functional_tool_type_id.id), + ('name', '=', cnc_processing.cutting_tool_name)]) + if functional_tools: + for functional_tool in functional_tools: + if functional_tool.on_tool_stock_num == 0: + self.env['sf.cnc.processing'].register_cnc_processing(cnc_processing) + if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0: + status = True + else: + status = True + if status: + self.env['sf.cam.work.order.program.knife.plan'].sudo().create({ + 'name': cnc_processing.workorder_id.production_id.name, + 'cam_procedure_code': cnc_processing.program_name, + 'filename': cnc_processing.cnc_id.name, + 'functional_tool_type_id': cnc_processing.functional_tool_type_id.id, + 'functional_tool_name': cnc_processing.cutting_tool_name, + 'cam_cutter_spacing_code': cnc_processing.cutting_tool_no, + 'process_type': cnc_processing.processing_type, + 'margin_x_y': float(cnc_processing.margin_x_y), + 'margin_z': float(cnc_processing.margin_z), + 'finish_depth': float(cnc_processing.depth_of_processing_z), + 'extension_length': float(cnc_processing.cutting_tool_extension_length), + 'shank_model': cnc_processing.cutting_tool_handle_type, + 'estimated_processing_time': cnc_processing.estimated_processing_time, + }) + class FunctionalToolAssembly(models.Model): _name = 'sf.functional.tool.assembly' diff --git a/sf_tool_management/models/maintenance_equipment.py b/sf_tool_management/models/maintenance_equipment.py index f5371a3f..1f42430c 100644 --- a/sf_tool_management/models/maintenance_equipment.py +++ b/sf_tool_management/models/maintenance_equipment.py @@ -1,9 +1,22 @@ -from odoo import models, api +from odoo import models, api, fields class SfMaintenanceEquipmentTool(models.Model): _inherit = 'maintenance.equipment.tool' + functional_tool_name_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具名称') + + image = fields.Binary('图片', related='functional_tool_name_id.image') + tool_code = fields.Char('功能刀具编码', related='functional_tool_name_id.code') + functional_tool_type = fields.Char('功能刀具类型', related='functional_tool_name_id.sf_cutting_tool_type_id.name') + tool_groups = fields.Char('刀具组', related='functional_tool_name_id.tool_groups_id.name') + diameter = fields.Integer('直径(mm)', related='functional_tool_name_id.functional_tool_diameter') + knife_tip_r_angle = fields.Float('刀尖R角(mm)', related='functional_tool_name_id.knife_tip_r_angle') + life_value_max = fields.Integer('最大寿命值(min)', related='functional_tool_name_id.max_lifetime_value') + alarm_value = fields.Integer('报警值(min)', related='functional_tool_name_id.alarm_value') + used_value = fields.Integer('已使用值(min)', related='functional_tool_name_id.used_value') + tool_install_time = fields.Datetime('机内装刀时间') + @api.model_create_multi def create(self, vals_list): tools = super().create(vals_list) diff --git a/sf_tool_management/models/mrp_workorder.py b/sf_tool_management/models/mrp_workorder.py new file mode 100644 index 00000000..63dd1029 --- /dev/null +++ b/sf_tool_management/models/mrp_workorder.py @@ -0,0 +1,38 @@ +import json +import requests +from odoo import fields, models, api +from odoo.exceptions import ValidationError +from odoo.addons.sf_base.commons.common import Common + + +class CNCprocessing(models.Model): + _inherit = 'sf.cnc.processing' + _description = 'CNC加工用刀检测' + + # ==========MES装刀指令接口========== + def register_cnc_processing(self, cnc_processing): + create_url = '/AutoDeviceApi/FeedBackOut' + sf_sync_config = self.env['res.config.settings'].get_values() + token = sf_sync_config['token'] + sf_secret_key = sf_sync_config['sf_secret_key'] + headers = Common.get_headers(self, token, sf_secret_key) + strurl = sf_sync_config['sf_url'] + create_url + val = { + 'DeviceId': cnc_processing.workorder_id.machine_tool_name, + 'RfidCode': None, + 'ToolId': cnc_processing.cutting_tool_no + } + kw = json.dumps(val, ensure_ascii=False) + r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers) + ret = r.json() + if r == 200: + return "MES装刀指令发送成功" + else: + raise ValidationError("MES装刀指令发送失败") + + @api.model_create_multi + def create(self, vals): + obj = super(CNCprocessing, self).create(vals) + # 调用CAM工单程序用刀计划创建方法 + self.env['sf.cam.work.order.program.knife.plan'].create_cam_work_plan(obj) + return obj diff --git a/sf_tool_management/views/sf_maintenance_equipment.xml b/sf_tool_management/views/sf_maintenance_equipment.xml new file mode 100644 index 00000000..fd863a84 --- /dev/null +++ b/sf_tool_management/views/sf_maintenance_equipment.xml @@ -0,0 +1,32 @@ + + + + + sf_manufacturing_equipment.form + maintenance.equipment + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index dd8e3bf0..fb8b3a5e 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -756,21 +756,29 @@ + + + + + + + + + + + + + + + - - - - - - - - - - - - + +