Compare commits

..

16 Commits

Author SHA1 Message Date
马广威
19799aefe4 Accept Merge Request #1059: (release/release_1.3 -> master)
Merge Request: 优化工件上下产线及配送、优化编程单下发

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1059?initial=true
2024-06-04 22:28:57 +08:00
jinling.yang
f33d7b79fc 优化工件上下产线 2024-06-04 16:13:42 +08:00
jinling.yang
e9ab57854e 修改生成外协出入库单时,查询条件排序修改为根据create_date或id排序 2024-06-04 10:48:50 +08:00
jinling.yang
b70ec36365 修改生成外协出入库单时,查询条件排序修改为根据id排序 2024-06-04 10:33:40 +08:00
jinling.yang
3738b8d72b 中控请求接口超时改为10秒 2024-06-03 17:21:42 +08:00
jinling.yang
5fb9326d8b 中控请求接口超时改为30秒 2024-06-03 17:09:35 +08:00
jinling.yang
2ab9f37062 1.修复工件配送2.优化运送空料架.3.cloud下发编程单至sf时,将程序单pdf同步至制造订单的装夹预调工单的加工图纸上4.修复工件上下产线接口 2024-06-03 16:59:14 +08:00
马广威
d527bc6899 Accept Merge Request #1056: (release/release_1.2 -> master)
Merge Request: 1、优化功能刀具清单查询接口,新增功能刀具查询接口。

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1056?initial=true
2024-05-30 21:34:14 +08:00
yuxianghui
6405944cf1 1、优化功能刀具清单查询接口,新增功能刀具查询接口。 2024-05-30 15:22:30 +08:00
jinling.yang
caf7aaa560 Merge branch 'master' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf 2024-05-29 12:50:44 +08:00
jinling.yang
ab2978b4ab Merge branch 'hotfix/修复编程单下发接口' 2024-05-29 12:50:04 +08:00
马广威
0b9c856d56 Accept Merge Request #1050: (release/release_1_1 -> master)
Merge Request: hotfix工厂环境问题,详见描述

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1050?initial=true
2024-05-29 12:47:10 +08:00
jinling.yang
66dc7b540c Merge branch 'hotfix/修复编程单下发接口' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release_1_1 2024-05-29 12:44:06 +08:00
jinling.yang
decfd27401 1.修复cnc工单没有程序文件路径
2.修复编程单已下发,在工件配送时,提示未下发nc程序
2024-05-29 12:41:55 +08:00
yuxianghui
f71d62dad1 优化工单工序 2024-05-29 11:26:17 +08:00
yuxianghui
b7a11f0abe 修复 【制造订单的工单顺序错误,需排查处理bug】 问题 2024-05-29 09:34:41 +08:00
8 changed files with 199 additions and 84 deletions

View File

@@ -166,11 +166,13 @@ class Manufacturing_Connect(http.Controller):
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
return json.JSONEncoder().encode(res)
logging.info('workorder_state:%s' % workorder.state)
if workorder.state != 'ready':
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '工单未就绪'}
return json.JSONEncoder().encode(res)
work_equipment_id = request.env['maintenance.equipment'].sudo().search([('name', '=', equipment_id)],
limit=1)
logging.info('work_equipment_id:%s' % work_equipment_id.name)
if not work_equipment_id:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有找到该加工设备'}
return json.JSONEncoder().encode(res)
@@ -457,20 +459,21 @@ class Manufacturing_Connect(http.Controller):
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
domain = [
('feeder_station_destination_id.name', '=', ret['DeviceId']),
('workorder_id.rfid_code', '=', rfid_code),
('status', '=', '已配送'),
('type', '=', '上产线')
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工')
]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(domain,
order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线':
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
if order.production_id.production_line_state == '待上产线':
logging.info(
'wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已上产线'})
'制造订单产线状态:%s' % order.production_id.production_line_state)
order.production_id.write({'production_line_state': '已上产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search([
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
('production_id', '=', order.production_id.id)])
if workpiece_delivery.status == '待下发':
workpiece_delivery.write({'is_manual_work': True})
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
@@ -511,20 +514,20 @@ class Manufacturing_Connect(http.Controller):
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
domain = [
('workorder_id.rfid_code', '=', rfid_code),
('status', '=', '待下发'),
('type', '=', '下产线')
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工')
]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(domain,
order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '已上产线':
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
if order.production_id.production_line_state == '已上产线':
logging.info(
'wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已下产线'})
delivery_Arr.append(wd.id)
'制造订单产线状态:%s' % order.production_id.production_line_state)
order.production_id.write({'production_line_state': '已下产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search([
('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
('production_id', '=', order.production_id.id)])
delivery_Arr.append(workpiece_delivery.id)
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
@@ -534,10 +537,14 @@ class Manufacturing_Connect(http.Controller):
[('id', 'in', delivery_Arr)])
if delivery_workpiece:
logging.info('开始向agv下发下产线任务')
is_free = delivery_workpiece._check_avgsite_state()
if is_free is True:
delivery_workpiece._delivery_avg()
logging.info('agv下发下产线任务下发完成')
agv_site = request.env['sf.agv.site'].sudo().search([])
if agv_site:
has_site = agv_site.update_site_state()
if has_site is True:
is_free = delivery_workpiece._check_avgsite_state()
if is_free is True:
delivery_workpiece._delivery_avg()
logging.info('agv下发下产线任务下发完成')
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
except Exception as e:

View File

@@ -26,20 +26,28 @@ class AgvSetting(models.Model):
timestamp = int(time.time())
center_control_url += str(timestamp)
logging.info('工件配送-请求中控地址:%s' % center_control_url)
center_control_r = requests.get(center_control_url, params={}, headers=headers)
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 = '空闲'
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
class AgvTaskRoute(models.Model):

View File

@@ -5,7 +5,7 @@ import re
import requests
from itertools import groupby
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.exceptions import UserError,ValidationError
from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
@@ -483,10 +483,49 @@ class MrpProduction(models.Model):
def _reset_work_order_sequence(self):
for rec in self:
current_sequence = 1
sequence_list = {}
model_type_id = rec.product_id.product_model_type_id
if model_type_id:
tmpl_num = 1
# 成品工序
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
if product_routing_tmpl_ids:
for tmpl_id in product_routing_tmpl_ids:
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
# 表面工艺工序
# 模型类型的表面工艺工序模版
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
# 产品选择的表面工艺
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
process_dict = {}
if model_process_parameters_ids:
for process_parameters_id in model_process_parameters_ids:
process_id = process_parameters_id.process_id
for surface_tmpl_id in surface_tmpl_ids:
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
surface_tmpl_name, process_parameters_id.name)})
process_list = sorted(process_dict.keys())
for process_num in process_list:
sequence_list.update({process_dict.get(process_num): tmpl_num})
tmpl_num += 1
# 坯料工序
tmpl_num = 1
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
if embryo_routing_tmpl_ids:
for tmpl_id in embryo_routing_tmpl_ids:
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
else:
raise ValidationError('该产品没有选择【模版类型】!')
for work in rec.workorder_ids:
work.sequence = current_sequence
current_sequence += 1
if sequence_list.get(work.name):
work.sequence = sequence_list[work.name]
else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
# if work.name == '获取CNC加工程序':
# work.button_start()
# #work.fetchCNC()

View File

@@ -159,7 +159,7 @@ class ResMrpWorkOrder(models.Model):
('已完工', '已完工'),
], string='工序状态', default='待装夹', readonly='True')
# 加工图纸
processing_drawing = fields.Binary(string='加工图纸', related='production_id.part_drawing')
processing_drawing = fields.Binary(string='加工图纸')
@api.depends('production_id')
def _compute_save_name(self):
@@ -964,7 +964,7 @@ class CNCprocessing(models.Model):
_order = 'sequence_number,id'
cnc_id = fields.Many2one('ir.attachment')
sequence_number = fields.Char('序号')
sequence_number = fields.Integer('序号')
program_name = fields.Char('程序名')
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型')
cutting_tool_name = fields.Char('刀具名称')
@@ -1026,6 +1026,8 @@ class CNCprocessing(models.Model):
'cutting_tool_extension_length': obj['cutting_tool_extension_length'],
'cutting_tool_handle_type': obj['cutting_tool_handle_type'],
'estimated_processing_time': obj['estimated_processing_time'],
'program_path': obj['program_path'],
'cnc_id': obj['cnc_id'].id,
'remark': obj['remark']
})
return cnc_processing_str
@@ -1227,15 +1229,25 @@ class WorkPieceDelivery(models.Model):
status = fields.Selection(
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发')
is_cnc_program_down = fields.Boolean('程序是否下发', default=False)
is_manual_work = fields.Boolean('人工操作', default=False)
active = fields.Boolean(string="有效", default=True)
@api.model
def create(self, vals):
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
else:
vals['type'] = '运送空料架'
obj = super(WorkPieceDelivery, self).create(vals)
return obj
@api.constrains('name')
def _check_name(self):
if self.type == '运送空料架':
wd = self.sudo().search([('name', '=', self.name), ('id', '!=', self.id)])
if wd:
raise UserError("该名称已存在")
def action_delivery_history(self):
return {
'name': _('配送历史'),
@@ -1285,6 +1297,8 @@ class WorkPieceDelivery(models.Model):
is_not_route += 1
# else:
# raise UserError('请选择【任务路线】再进行配送')
# if item.production_id.production_line_state == '已下产线' and item.state == '待下发' and item.type == '下产线':
# raise UserError('该制造订单已下产线,无需配送')
if production_type != item.type:
raise UserError('请选择类型为%s的制造订单进行配送' % production_type)
if down_status != item.status:
@@ -1338,22 +1352,25 @@ class WorkPieceDelivery(models.Model):
is_free = False
agv_site = self.env['sf.agv.site'].search([])
if agv_site:
agv_site.update_site_state()
for item in self:
logging.info('工件配送-起点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if item.type in ['上产线', '下产线']:
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
has_site = agv_site.update_site_state()
if has_site is True:
for item in self:
if item.type in ['上产线', '下产线']:
logging.info('工件配送-点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-终点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
# 配送至avg小车
def _delivery_avg(self):
@@ -1416,7 +1433,7 @@ class WorkPieceDelivery(models.Model):
'task_delivery_time': fields.Datetime.now(),
'status': '待配送'
})
if delivery_item == "上产线":
if delivery_item.type == "上产线":
delivery_item.workorder_id.write({'is_delivery': True})
else:
raise UserError(ret['message'])

View File

@@ -492,7 +492,7 @@ class StockPicking(models.Model):
# 设置外协出入单的名称
def _get_name_Res(self, rescode):
last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date DESC', limit=1)
last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date desc,id desc', limit=1)
if not last_picking:
num = "%04d" % 1
else:

View File

@@ -659,7 +659,9 @@
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
<field name="context">{'search_default_on_up':1}</field>
<field name="view_mode">tree,search</field>
<field name="domain">[('type','in',['上产线','下产线']),('workorder_state','=','done')]</field>
<field name="domain">
[('type','in',['上产线','下产线']),('workorder_state','=','done'),('is_manual_work','=',false)]
</field>
</record>
@@ -668,14 +670,18 @@
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="工件配送" class="center" create="0" edit="0" delete="0">
<tree string="工件配送" editable="bottom" class="center" delete="0" create="1">
<header>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
</header>
<field name="name" string="路线名称" readonly="1"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="feeder_station_start_id" readonly="1"/>
<field name="feeder_station_destination_id" readonly="1"/>
<field name="name" string="路线名称" attrs="{'readonly': [('id', '!=', False)]}"
placeholder="例如:运送空料架路线:G01-A01" required="1" force_save="1"/>
<field name="route_id" options="{'no_create': True}" required="1"
attrs="{'readonly': [('id', '!=', False)]}" domain="[('route_type', '=', '运送空料架')]"
force_save="1"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<!-- <field name="type" readonly="1"/>-->
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
string="历史"/>
</tree>

View File

@@ -63,12 +63,19 @@ class Sf_Mrs_Connect(http.Controller):
logging.info('cnc_processing111:%s' % cnc_processing)
if cnc_processing:
cnc_processing_arr.append(cnc_processing._json_cnc_processing(cnc_processing))
if cnc_program and cnc_processing_arr:
if (cnc_program and cnc_processing_arr) or (not cnc_program and cnc_processing_arr):
logging.info('cnc_processing_arr:%s' % cnc_processing_arr)
cnc_program.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program.workorder_ids.filtered(lambda b: b.routing_type == 'CNC加工').write(
{'cnc_ids': cnc_processing_arr, 'cnc_worksheet': cnc_production.workorder_ids.filtered(
lambda b: b.routing_type == 'CNC加工').cnc_worksheet})
if cnc_program and cnc_processing_arr:
cnc_program.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program.workorder_ids.filtered(lambda b1: b1.routing_type == '装夹预调').write(
{'processing_drawing': cnc_production.workorder_ids.filtered(
lambda b1: b1.routing_type == 'CNC加工').cnc_worksheet})
cnc_program.workorder_ids.filtered(lambda b: b.routing_type == 'CNC加工').write(
{'cnc_ids': cnc_processing_arr, 'cnc_worksheet': cnc_production.workorder_ids.filtered(
lambda b: b.routing_type == 'CNC加工').cnc_worksheet})
cnc_program |= cnc_production
if not cnc_program and cnc_processing_arr:
cnc_program = cnc_production
cnc_program_ids = [item.id for item in cnc_program]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('production_id', 'in', cnc_program_ids)])

View File

@@ -8,31 +8,62 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/ToolInventory', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_functional_tool_groups_Info(self, **kw):
def get_functional_tool_inventory_Info(self, **kw):
"""
刀具组接口
功能刀具清单接口
:param kw:
:return:
"""
logging.info('get_functional_tool_groups_Info:%s' % kw)
logging.info('get_functional_tool_inventory_Info:%s' % kw)
try:
datas = request.httprequest.data
ret = json.loads(datas)
# ret = json.loads(ret['result'])
logging.info('DeviceId:%s' % ret)
functional_tools = request.env['sf.tool.inventory'].sudo().search([])
tool_inventory = request.env['sf.tool.inventory'].sudo().search([])
res = {'Succeed': True, 'Datas': []}
if tool_inventory:
for item in tool_inventory:
res['Datas'].append({
'ToolName': item.name,
'GroupName': item.tool_groups_id.name,
'Lifetime': item.life_span
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('get_functional_tool_inventory_Info error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/ToolEntity', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_functional_tool_entity_Info(self, **kw):
"""
功能刀具列表接口
:param kw:
:return:
"""
logging.info('get_functional_tool_entity_Info:%s' % kw)
try:
datas = request.httprequest.data
ret = json.loads(datas)
# ret = json.loads(ret['result'])
logging.info('DeviceId:%s' % ret)
functional_tools = request.env['sf.functional.cutting.tool.entity'].sudo().search([])
res = {'Succeed': True, 'Datas': []}
if functional_tools:
for item in functional_tools:
res['Datas'].append({
'Rfid': item.rfid,
'ToolName': item.tool_name_id.name,
'GroupName': item.tool_groups_id.name,
'ToolId': item.functional_cutting_tool_model_id.name,
'ToolName': item.name
'MaxLifetime': item.max_lifetime_value,
'KnifeHandle': item.cutting_tool_cutterhandle_model_id.name
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('get_functional_tool_groups_Info error:%s' % e)
logging.info('get_functional_tool_entity_Info error:%s' % e)
return json.JSONEncoder().encode(res)