Merge branch 'feature/优化快速订单' into develop

This commit is contained in:
jinling.yang
2024-02-21 14:54:58 +08:00
16 changed files with 853 additions and 44 deletions

View File

@@ -21,6 +21,7 @@ class Http(models.AbstractModel):
def _auth_method_sf_token(cls):
# 从headers.environ中获取对方传过来的token,timestamp,加密的校验字符串
datas = request.httprequest.headers.environ
logging.info(datas)
if 'HTTP_TOKEN' in datas:
_logger.info('token:%s' % datas['HTTP_TOKEN'])
# 查询密钥
@@ -40,6 +41,7 @@ class Http(models.AbstractModel):
raise AuthenticationError('请求已过期')
check_str = '%s%s%s' % (datas['HTTP_TOKEN'], post_time, factory_secret.sf_secret_key)
check_sf_str = hashlib.sha1(check_str.encode('utf-8')).hexdigest()
_logger.info('check_str:%s' % check_sf_str)
if check_sf_str != datas['HTTP_CHECKSTR']:
raise AuthenticationError('数据校验不通过')
else:

View File

@@ -8,14 +8,14 @@
<field name="arch" type="xml">
<xpath expr="//div[hasclass('app_settings_block')]/div" position="before">
<div>
<h2>bfm环境配置</h2>
<h2>业务平台参数配置</h2>
<div class="row mt16 o_settings_container" id="jd_api">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="bfm_url"/>
<field name="bfm_url"/>
<label for="bfm_url" />
<field name="bfm_url" string="访问地址"/>
</div>
</div>
<!-- </div> -->

View File

@@ -20,9 +20,9 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
logging.info('RfidCode:%s' % ret)
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
[('production_id.name', '=', 'WH/MO/00071'), ('routing_type', '=', '装夹')])
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', '装夹预调')])
if workorder:
for item in workorder:
res['Datas'].append({

View File

@@ -133,6 +133,7 @@ class ResMrpWorkOrder(models.Model):
preset_program_information = fields.Char(string="预调程序信息")
workpiece_delivery_ids = fields.One2many('sf.workpiece.delivery', 'workorder_id', '工件配送')
is_delivery = fields.Boolean('是否配送完成', default=False)
rfid_code = fields.Char('RFID')
@api.onchange('is_ok')
def _onchange_inspection_user_id(self):

View File

@@ -176,6 +176,8 @@
attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name="functional_fixture_type_id" force_save="1"
attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name="rfid_code"
attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
</group>
<group attrs='{"invisible": [("routing_type","=","获取CNC加工程序")]}'>
<div>

View File

@@ -13,8 +13,8 @@ class ResConfigSettings(models.TransientModel):
token = fields.Char(string='TOKEN', default='b811ac06-3f00-11ed-9aed-0242ac110003')
sf_secret_key = fields.Char(string='密钥', default='wBmxej38OkErKhD6')
sf_url = fields.Char(string='访问地址', default='https://sf.cs.jikimo.com')
bfm_url = fields.Char(string='业务平台后端访问地址', default='https://bfm.jikimo.com')
agv_url = fields.Char(string='avg访问地址', default='http://IP:PORT/rcms/services/rest')
model_parser_url = fields.Char('特征识别路径')
ftp_host = fields.Char(string='FTP的ip')
ftp_port = fields.Char(string='FTP端口')
ftp_user = fields.Char(string='FTP用户')

View File

@@ -8,8 +8,8 @@
<field name="arch" type="xml">
<xpath expr="//div[hasclass('app_settings_block')]/div" position="before">
<div>
<h2>同步参数配置</h2>
<div class="row mt16 o_settings_container" id="pay_api">
<h2>云平台参数配置</h2>
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
@@ -36,7 +36,7 @@
</div>
<div>
<h2>FTP参数配置</h2>
<div class="row mt16 o_settings_container" id="pay_api">
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
@@ -61,14 +61,14 @@
</div>
</div>
<div>
<h2>业务平台参数配置</h2>
<div class="row mt16 o_settings_container" id="pay_api">
<h2>特征识别参数配置</h2>
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="bfm_url" string="访问地址"/>
<field name="bfm_url"/>
<label for="model_parser_url" string="访问地址"/>
<field name="model_parser_url"/>
</div>
</div>
</div>

View File

@@ -1,3 +1,6 @@
from . import sale_order
from . import quick_easy_order
from . import auto_quatotion_common
from . import parser_and_calculate_work_time
from . import preload_datas_functions

View File

@@ -0,0 +1,472 @@
import time
# import pandas as pd
from lxml import etree
from collections import Counter
from . import preload_datas_functions as preload
# import preload_datas_functions as preload
class FeatureParser:
"""
解析Feature.xml文件
"""
def __init__(self, xml_file):
self.root = etree.parse(xml_file).getroot()
self.size = self._get_size()
self.holes = self._get_holes()
self.slots = self._get_slot()
self.open_slots = self._get_open_slot()
self.vectors = self._get_vectors()
def _get_size(self):
size = self.root.find('Size')
return {
'length': float(size.get('Length')),
'width': float(size.get('Width')),
'height': float(size.get('Height'))
}
def _get_vectors(self):
vectors = {}
for item in self.root.findall('.//Item'):
vector = item.find('Vector')
if vector is not None:
key = (vector.get('i'), vector.get('j'), vector.get('k'))
vectors[key] = vectors.get(key, 0) + 1
return vectors
def get_vector_counts(self):
return len(self.vectors)
def _get_holes(self):
holes = []
hole_element = self.root.find('Hole')
if hole_element is not None:
for item in self.root.find('Hole').iter('Item'):
hole = {} # 每个hole是一个字典
hole['id'] = int(item.get('ID'))
hole['name'] = item.get('Name')
hole['red'] = int(item.get('Red'))
hole['green'] = int(item.get('Green'))
hole['blue'] = int(item.get('Blue'))
# 处理circles
circles = []
for circle in item.iter('Circle'):
circles.append({
'x': float(circle.get('x')),
'y': float(circle.get('y')),
'z': float(circle.get('z')),
'rad': float(circle.get('rad'))
})
hole['circles'] = circles
# 处理bottom
bottoms = []
for bottom in item.iter('Bottom'):
bottoms.append({
'x': float(bottom.get('x')),
'y': float(bottom.get('y')),
'z': float(bottom.get('z')),
'rad': float(bottom.get('rad'))
})
hole['bottoms'] = bottoms
# 处理vector
for vector in item.iter('Vector'):
hole['vector'] = {
'i': float(vector.get('i')),
'j': float(vector.get('j')),
'k': float(vector.get('k'))
}
# 创建元组并添加到列表中
z_rad_tuples = []
max_z = None
non_zero_rads = set() # 使用set来存储rad值自动去重
for circle in circles:
z = float(circle.get('z'))
rad = float(circle.get('rad'))
if max_z is None or z > max_z:
max_z = z
if rad != 0:
non_zero_rads.add(rad)
for rad in non_zero_rads:
z_rad_tuple = (max_z, rad)
z_rad_tuples.append(z_rad_tuple)
hole['z_rad_tuples'] = z_rad_tuples
holes.append(hole) # 添加到holes列表中
return holes
def _get_slot(self):
"""
获取slot信息
"""
slots = []
slot_a_list = []
slot_element = self.root.find('Slot')
if slot_element is not None:
for item in self.root.find('Slot').iter('Item'):
slot = {}
slot['id'] = int(item.get('ID'))
slot['name'] = item.get('Name')
slot['red'] = int(item.get('Red'))
slot['green'] = int(item.get('Green'))
slot['blue'] = int(item.get('Blue'))
# 获取Volume和Area信息
volume = item.find('Volume')
if volume is not None:
slot['volume'] = float(volume.get('value'))
area = item.find('Area')
if area is not None:
slot['area'] = float(area.get('value'))
slot_a_list.append(slot['area'])
# 处理lines
lines = []
for line in item.iter('Line'):
lines.append({
'type': line.get('Type'), # 'type' : 'line' or 'arc
'x1': float(line.get('x1')),
'y1': float(line.get('y1')),
'z1': float(line.get('z1')),
'x2': float(line.get('x2')),
'y2': float(line.get('y2')),
'z2': float(line.get('z2'))
})
slot['lines'] = lines
# 处理Arc
arcs = []
for arc in item.iter('Arc'):
arcs.append({
'type': arc.get('Type'),
'x1': float(arc.get('x1')),
'y1': float(arc.get('y1')),
'z1': float(arc.get('z1')),
'x2': float(arc.get('x2')),
'y2': float(arc.get('y2')),
'z2': float(arc.get('z2')),
'x3': float(arc.get('x3')),
'y3': float(arc.get('y3')),
'z3': float(arc.get('z3'))
})
slot['arcs'] = arcs
slot['a'] = slot_a_list
slots.append(slot)
return slots
def _get_open_slot(self):
"""
获取open_slot信息
"""
open_slots = []
open_slot_v_list = []
open_slot_element = self.root.find('OpenSlot')
if open_slot_element is not None:
for item in self.root.find('OpenSlot').iter('Item'):
open_slot = {}
open_slot['id'] = int(item.get('ID'))
open_slot['name'] = item.get('Name')
open_slot['red'] = int(item.get('Red'))
open_slot['green'] = int(item.get('Green'))
open_slot['blue'] = int(item.get('Blue'))
# 获取Volume和Area信息
volume = item.find('Volume')
if volume is not None:
open_slot['volume'] = float(volume.get('value'))
area = item.find('Area')
if area is not None:
open_slot['area'] = float(area.get('value'))
# open_slot_v_list.append(round(open_slot['volume'] / open_slot['area'], 3))
open_slot_v_list.append(open_slot['area'])
# 处理lines
lines = []
for line in item.iter('Line'):
lines.append({
'type': line.get('Type'), # 'type' : 'line' or 'arc
'x1': float(line.get('x1')),
'y1': float(line.get('y1')),
'z1': float(line.get('z1')),
'x2': float(line.get('x2')),
'y2': float(line.get('y2')),
'z2': float(line.get('z2'))
})
open_slot['lines'] = lines
# 处理Arc
arcs = []
for arc in item.iter('Arc'):
arcs.append({
'type': arc.get('Type'),
'x1': float(arc.get('x1')),
'y1': float(arc.get('y1')),
'z1': float(arc.get('z1')),
'x2': float(arc.get('x2')),
'y2': float(arc.get('y2')),
'z2': float(arc.get('z2')),
'x3': float(arc.get('x3')),
'y3': float(arc.get('y3')),
'z3': float(arc.get('z3'))
})
open_slot['arcs'] = arcs
open_slot['v'] = open_slot_v_list
open_slots.append(open_slot)
return open_slots
def hole_time(parser):
"""
计算孔的工时
:return:
"""
# 判断是否有孔
if parser.holes is not None:
# 遍历所有的孔,获取孔径和孔深度,然后调用函数查询工时
hole_total_time = 0
nums = 1
expand_hole = ''
j_time = 0
j_hole_nums = 0
hole_nums = 0
for hole in parser.holes:
for z_rad_tuple in hole['z_rad_tuples']:
if (2 * z_rad_tuple[1] * z_rad_tuple[0] <= 3750) and (2 * z_rad_tuple[1] <= 25):
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = preload.get_suitable_hole_working_hours(preload.df_hole_duration,
2 * z_rad_tuple[1],
z_rad_tuple[0])
# if per_time_minute is None:
# raise Exception('孔径为%s深度为%s的孔没有找到对应的工时' % (2 * z_rad_tuple[1], z_rad_tuple[0]))
# print('per_time_minute', per_time_minute)
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
elif (2 * z_rad_tuple[1] * z_rad_tuple[0] <= 3750) and (2 * z_rad_tuple[1] > 25):
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = 0.0003 * 2 * z_rad_tuple[1] * z_rad_tuple[0]
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
elif 3750 < 2 * z_rad_tuple[1] * z_rad_tuple[0] <= 50000:
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = 0.0003 * 2 * z_rad_tuple[1] * z_rad_tuple[0]
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
elif 50000 < 2 * z_rad_tuple[1] * z_rad_tuple[0] <= 100000:
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = 0.00018 * 2 * z_rad_tuple[1] * z_rad_tuple[0]
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
elif 100000 < 2 * z_rad_tuple[1] * z_rad_tuple[0] <= 150000:
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = 0.00016 * 2 * z_rad_tuple[1] * z_rad_tuple[0]
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
elif 150000 < 2 * z_rad_tuple[1] * z_rad_tuple[0] <= 200000:
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = 0.00015 * 2 * z_rad_tuple[1] * z_rad_tuple[0]
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
elif 200000 < 2 * z_rad_tuple[1] * z_rad_tuple[0] <= 250000:
hole_nums += 1
# print('z_rad_tuple', z_rad_tuple)
per_time_minute = 0.0002 * 2 * z_rad_tuple[1] * z_rad_tuple[0]
expand_hole_end = 0.6 if expand_hole == '' else 1
per_time = (per_time_minute * 1 * expand_hole_end + j_time * j_hole_nums * expand_hole_end) / 60
hole_total_time += per_time
else:
raise Exception('孔径为%s,深度为%s的孔没有找到对应的工时' % (2 * z_rad_tuple[1], z_rad_tuple[0]))
print('孔工时', round(hole_total_time * nums * 2) / 2)
print('共有%s个孔,其中%s为台阶孔' % (len(parser.holes), hole_nums - len(parser.holes)))
return round(hole_total_time * nums * 2) / 2
else:
return 0
def slot_time(parser):
# 判断是否有槽
if parser.slots is not None:
# 遍历所有的槽,获取槽的长度,然后调用函数查询工时
slot_total_time = 0
nums = 1
finish_time = 0
process_time = 0
process_total_time = 0
slot_a = parser.slots[0]['a']
slot_a_counter = Counter(slot_a)
slot_a_counter_result = dict(slot_a_counter)
for i in slot_a_counter_result:
for slot in parser.slots:
if slot['area'] == i:
# # 计算长度第一条线和第三条线的X轴距离
# length = abs(slot['lines'][0]['y1'] - slot['lines'][2]['y1'])
# # 计算宽度第一条线和第二条线的Y轴距离
# width = abs(slot['lines'][1]['x1'] - slot['lines'][3]['x1'])
# # 计算面积
# area = length * width
# 槽深度
depth = round(slot['volume'] / slot['area'], 3)
# 沟通刀具暂定为12
finish_tool_diameter = 12
if 200 < slot['area'] <= 5000:
finish_time = 0
# 加工穴数待定取得每一个槽的穴数和装夹次数那这个数量目前暂时按1来算待有统计数据之后再说
rough_part_nums = slot_a_counter_result[slot['area']]
rough_clamping_times = 1
finishi_part_nums = 1
finish_clamping_times = 1
# 调用函数计算槽的工时
slot_total_time = preload.get_suitable_rough_working_hours(preload.df_rough_duration, depth)
# if nums > 20:
# process_time = round(
# (nums * 0.6 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
# finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# elif nums > 10:
# process_time = round(
# (nums * 0.7 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
# finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# elif nums > 6:
# process_time = round(
# (nums * 0.8 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
# finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# elif nums > 4:
# process_time = round(
# (nums * 0.9 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
# finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# elif nums > 1:
# process_time = round(
# (nums * 0.95 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
# finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# else:
# process_time = round(
# (nums * 1 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
# finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
process_time = round(
(nums * 1 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# print('slot_total_time', slot_total_time)
elif slot['area'] <= 200:
slot_total_time = 0
rough_part_nums = slot_a_counter_result[slot['area']]
rough_clamping_times = 1
finishi_part_nums = 1
finish_clamping_times = 1
# 调用函数计算槽的工时
finish_time = preload.get_suitable_finish_working_hours(preload.df_finish_duration, depth,
finish_tool_diameter)
process_time = round(
(nums * 1 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# print('finish_time', finish_time)
else:
rough_part_nums = slot_a_counter_result[slot['area']]
rough_clamping_times = 1
finishi_part_nums = 1
finish_clamping_times = 1
process_time = round(
(nums * 1 * ((0.00016 * rough_part_nums + rough_clamping_times * 20) + (
finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
process_total_time += process_time
print('槽工时', process_total_time)
return process_total_time
else:
return 0
def open_slot_time(parser):
# 判断是否有开口槽
if parser.open_slots is not None:
# 遍历所有的开口槽,获取槽宽和槽长,然后调用函数查询工时
open_slot_total_time = 0
nums = 1
finish_time = 0
open_slot_process_time = 0
open_slot_v = parser.open_slots[0]['v']
counter = Counter(open_slot_v)
result = dict(counter)
transiant_time = 0
for i in result:
for open_slot in parser.open_slots:
if open_slot['area'] == i:
depth = round(open_slot['volume'] / open_slot['area'], 3)
# 沟通刀具暂定为12
finish_tool_diameter = 12
if 200 < open_slot['area'] <= 5000:
finish_time = 0
# 加工穴数待定取得每一个槽的穴数和装夹次数那这个数量目前暂时按1来算待有统计数据之后再说
rough_part_nums = result[open_slot['area']]
rough_clamping_times = 1
finishi_part_nums = 1
finish_clamping_times = 1
# 调用函数计算槽的工时
slot_total_time = preload.get_suitable_rough_working_hours(preload.df_rough_duration, depth)
open_slot_process_time = round(
(nums * 1 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# print('slot_total_time', slot_total_time)
elif open_slot['area'] <= 200:
slot_total_time = 0
rough_part_nums = 1
rough_clamping_times = 1
finishi_part_nums = 1
finish_clamping_times = 1
# 调用函数计算槽的工时
finish_time = preload.get_suitable_finish_working_hours(preload.df_finish_duration, depth,
finish_tool_diameter)
open_slot_process_time = round(
(nums * 1 * ((slot_total_time * rough_part_nums + rough_clamping_times * 20) + (
finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
# print('finish_time', finish_time)
else:
rough_part_nums = 1
rough_clamping_times = 1
finishi_part_nums = 1
finish_clamping_times = 1
open_slot_process_time = round(
(nums * 1 * ((0.00016 * rough_part_nums + rough_clamping_times * 20) + (
finish_time * finishi_part_nums + finish_clamping_times * 25)) / 60) * 2) / 2
transiant_time += open_slot_process_time
print('开口槽工时', transiant_time)
return transiant_time
else:
return 0
if __name__ == '__main__':
time1 = time.time()
parser = FeatureParser(
'D:\\ccccccccccccccccccccccccccccccccccccccccccccccc\\aa\\JKM001-260.200.30_FeatureTable.xml')
# print('parser', parser.holes)
# print('parser.slots', parser.slots)
# print('parser.open_slots', parser.open_slots)
print('总工时', hole_time(parser) + slot_time(parser) + open_slot_time(parser))
time2 = time.time()
print('耗时:', time2 - time1)

View File

@@ -0,0 +1,201 @@
import psycopg2
# import pandas as pd
def load_and_convert_data(table_name, column_names):
connection = None
data = []
try:
# connection = psycopg2.connect(user="odoo",
# password="odoo",
# host="localhost",
# port="5432",
# database="www1")
connection = psycopg2.connect(user="odoo",
password="odoo",
host="120.76.195.146",
port="15432",
database="bfm_dev1")
cursor = connection.cursor()
# Construct the query string using the table name passed in
query = f"SELECT {', '.join(column_names)} FROM {table_name};"
cursor.execute(query)
# Fetch all rows from cursor
data = cursor.fetchall()
except (Exception, psycopg2.Error) as error:
print("Error fetching data from PostgreSQL table", error)
finally:
# Always close database connection after work done
if (connection):
cursor.close()
connection.close()
# Convert the list of tuples to DataFrame
# df = pd.DataFrame(data, columns=column_names)
#
# # Convert all string columns to float
# for col in df.columns:
# if df[col].dtype == 'object':
# df[col] = df[col].astype(float)
return 'df'
def get_suitable_hole_working_hours(df, target_diameter, target_depth):
"""
从钻孔、铰孔数据中获取符合要求的最小工时
"""
# df为输入的数据target_diameter为目标孔径target_depth为目标孔深
df_diameter_filtered = df.loc[df['hole_diameter'] >= target_diameter]
if not df_diameter_filtered.empty:
min_diameter = df_diameter_filtered['hole_diameter'].min()
df_depth_filtered = df_diameter_filtered.loc[
(df_diameter_filtered['hole_diameter'] == min_diameter) & (
df_diameter_filtered['hole_depth'] >= target_depth)]
if not df_depth_filtered.empty:
min_depth_row = df_depth_filtered.loc[df_depth_filtered['hole_depth'].idxmin()]
min_working_hours = min_depth_row['working_hours']
return min_working_hours
else:
print("No records found where hole_depth is bigger than the target depth")
return None
else:
print("No records found where hole_diameter is bigger than the target diameter")
return None
def get_suitable_blank_working_hours(df, blank_height, blank_length, blank_width):
"""
从毛坯数据中获取符合要求的最小工时
"""
# df为输入的数据blank_height为目标毛坯高度blank_length为目标毛坯长度blank_width为目标毛坯宽度
df_height_filtered = df.loc[df['blank_height'] >= blank_height]
if not df_height_filtered.empty:
min_height = df_height_filtered['blank_height'].min()
df_length_filtered = df_height_filtered.loc[
(df_height_filtered['blank_height'] == min_height) & (
df_height_filtered['blank_length'] >= blank_length)]
if not df_length_filtered.empty:
min_length_row = df_length_filtered.loc[df_length_filtered['blank_length'].idxmin()]
min_working_hours = min_length_row['working_hours']
return min_working_hours
else:
print("No records found where blank_length is bigger than the target length")
return None
else:
print("No records found where blank_height is bigger than the target height")
return None
def get_suitable_rough_working_hours(df, rough_depth):
"""
从粗加工数据中获取符合要求的最小工时
"""
# df为输入的数据rough_depth为目标粗加工深度
df_depth_filtered = df.loc[df['rough_depth'] >= rough_depth]
if not df_depth_filtered.empty:
min_depth_row = df_depth_filtered.loc[df_depth_filtered['rough_depth'].idxmin()]
min_working_hours = min_depth_row['working_hours']
return min_working_hours
else:
print("No records found where rough_depth is bigger than the target depth")
return None
def get_suitable_finish_working_hours(df, finish_depth, finish_tool_diameter):
"""
从精加工数据中获取符合要求的最小工时
"""
# df为输入的数据finish_depth为目标精加工深度finish_tool_diameter为目标精加工刀具直径
df_depth_filtered = df.loc[df['finish_depth'] >= finish_depth]
if not df_depth_filtered.empty:
min_depth = df_depth_filtered['finish_depth'].min()
df_tool_diameter_filtered = df_depth_filtered.loc[
(df_depth_filtered['finish_depth'] == min_depth) & (
df_depth_filtered['finish_tool_diameter'] >= finish_tool_diameter)]
if not df_tool_diameter_filtered.empty:
min_tool_diameter_row = df_tool_diameter_filtered.loc[
df_tool_diameter_filtered['finish_tool_diameter'].idxmin()]
min_working_hours = min_tool_diameter_row['working_hours']
return min_working_hours
else:
print("No records found where finish_tool_diameter is bigger than the target tool diameter")
return None
else:
print("No records found where finish_depth is bigger than the target depth")
return None
def get_suitable_chamfer_working_hours(df, chamfer_length, chamfer_size):
"""
根据倒角长度获得倒角工时装夹平面耗时clamping_type_plane和装夹斜面耗时clamping_type_slope
"""
# df为输入的数据chamfer_length为目标倒角长度clamping_type_plane为目标装夹平面耗时clamping_type_slope为目标装夹斜面耗时
df_length_filtered = df.loc[df['chamfer_length'] >= chamfer_length]
df_chamfer_size_filtered = df.loc[df['chamfer_size'] >= chamfer_size]
if not df_length_filtered.empty and not df_chamfer_size_filtered.empty:
min_length_row = df_length_filtered.loc[df_length_filtered['chamfer_length'].idxmin()]
min_chamfer_size_row = df_chamfer_size_filtered.loc[df_chamfer_size_filtered['chamfer_size'].idxmin()]
clamping_time = min_length_row['clamping_time']
clamping_type_plane = min_length_row['clamping_type_plane']
clamping_type_slope = min_length_row['clamping_type_slope']
coefficient = min_chamfer_size_row['coefficient']
return clamping_time, clamping_type_plane, clamping_type_slope, coefficient
else:
print("No records found where chamfer_length is bigger than the target length")
return None
df_hole_duration = load_and_convert_data('hole_duration', ['hole_diameter', 'hole_depth', 'working_hours'])
df_j_hole_duration = load_and_convert_data('j_hole_duration', ['hole_diameter', 'hole_depth', 'working_hours'])
df_chamfer_duration = load_and_convert_data('chamfer_duration',
['chamfer_length', 'clamping_time', 'chamfer_size', 'coefficient',
'clamping_type_plane', 'clamping_type_slope'])
df_blank_duration = load_and_convert_data('blank_duration',
['blank_length', 'blank_width', 'blank_height', 'working_hours'])
df_rough_duration = load_and_convert_data('rough_duration', ['rough_depth', 'working_hours'])
df_finish_duration = load_and_convert_data('finish_duration',
['finish_depth', 'finish_tool_diameter', 'working_hours'])
if __name__ == '__main__':
min_working_hours = get_suitable_hole_working_hours(df_hole_duration, 24, 150)
print('min_working_hours', min_working_hours)
min_j_working_hours = get_suitable_hole_working_hours(df_j_hole_duration, 10, 15)
print('min_j_working_hours', min_j_working_hours)
min_blank_working_hours = get_suitable_blank_working_hours(df_blank_duration, 150, 300, 300)
print('min_blank_working_hours', min_blank_working_hours)
min_rough_working_hours = get_suitable_rough_working_hours(df_rough_duration, 49)
print('min_rough_working_hours', min_rough_working_hours)
min_finish_working_hours = get_suitable_finish_working_hours(df_finish_duration, 0.5, 10)
print('min_finish_working_hours', min_finish_working_hours)
clamping_time, clamping_type_plane, clamping_type_slope, coefficient = get_suitable_chamfer_working_hours(
df_chamfer_duration, 10, 1.5)
print('clamping_time', clamping_time)
print('clamping_type_plane', clamping_type_plane)
print('clamping_type_slope', clamping_type_slope)
print('coefficient', coefficient)

View File

@@ -2,17 +2,19 @@ import logging
import base64
import hashlib
import os
import platform
import json
from datetime import datetime
import requests
from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file
from odoo import http
from odoo.http import request
# from OCC.Extend.DataExchange import read_step_file
# from OCC.Extend.DataExchange import write_stl_file
from odoo import models, fields, api
from odoo.modules import get_resource_path
from odoo.exceptions import ValidationError, UserError
from odoo.addons.sf_base.commons.common import Common
from . import parser_and_calculate_work_time as pc
@@ -34,8 +36,8 @@ class QuickEasyOrder(models.Model):
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')], string='加工精度', default='0.10')
material_id = fields.Many2one('sf.production.materials', '材料', compute='_compute_material_model', store=True)
material_model_id = fields.Many2one('sf.materials.model', '型号', compute='_compute_material_model', store=True)
material_id = fields.Many2one('sf.production.materials', '材料')
material_model_id = fields.Many2one('sf.materials.model', '型号')
# process_id = fields.Many2one('sf.production.process', string='表面工艺')
parameter_ids = fields.Many2many('sf.production.process.parameter', 'process_item_order_rel', string='可选参数')
quantity = fields.Integer('数量', default=1)
@@ -77,11 +79,11 @@ class QuickEasyOrder(models.Model):
if len(item[2]) > 0:
logging.info('create-attachment:%s' % int(item[2][0]))
attachment = self.env['ir.attachment'].sudo().search([('id', '=', int(item[2][0]))])
base64_data = base64.b64encode(attachment.datas)
base64_datas = base64_data.decode('utf-8')
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
report_path = attachment._full_path(attachment.store_fname)
vals['model_file'] = self.transition_glb_file(report_path, model_code)
# base64_data = base64.b64encode(attachment.datas)
# base64_datas = base64_data.decode('utf-8')
# model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# report_path = attachment._full_path(attachment.store_fname)
vals['model_file'] = self.model_analyze(attachment)
# logging.info('create-model_file:%s' % len(vals['model_file']))
obj = super(QuickEasyOrder, self).create(vals)
@@ -91,6 +93,147 @@ class QuickEasyOrder(models.Model):
obj.state = '待接单'
return obj
def model_analyze(self,model_attachment):
"""
step模型解析上传模型时转为web可显示的格式
:return:
"""
config = request.env['res.config.settings'].sudo().get_values()
try:
# 获取当前操作系统
os_name = platform.system()
for item in model_attachment:
# 将拿到的3D模型数据存入文件
# 定义文件名和文件的二进制内容
file_name = item.name # 请将这里替换为你的文件名
print('file_name', file_name)
# base64_data = base64.b64encode(item.datas)
# base64_datas = base64_data.decode('utf-8')
binary_content = item.datas # 请将这里替换为你的文件的二进制内容
# binary_content从字符串转为二进制
binary_content = base64.b64decode(binary_content)
# 定义新的文件夹路径
# 根据操作系统不同,文件路径不同
path_header = '/model_parser' if os_name == 'Linux' else 'D:/model_analysis'
# new_folder_path = 'D:/11111final' + '/' + item['name'].split(".")[0]
new_folder_path = path_header + '/' + item.name.rpartition('.')[0]
print('new_folder_path', new_folder_path)
# 检查新的文件夹是否存在,如果不存在,则创建
if not os.path.exists(new_folder_path):
os.makedirs(new_folder_path)
# 定义新的文件路径
new_file_path = os.path.join(new_folder_path, file_name)
# 将二进制内容写入新的文件
with open(new_file_path, 'wb') as f:
f.write(binary_content)
# 检查文件是否已经成功写入
if os.path.exists(new_file_path):
print(f'Successfully wrote binary content to {new_file_path}')
else:
print(f'Failed to write binary content to {new_file_path}')
# 附件
# attachment = request.env['ir.attachment'].sudo().create({
# 'datas': item['data'].encode('utf-8'),
# 'type': 'binary',
# 'description': '模型文件',
# 'name': item['name'],
# 'public': True,
# 'model_name': item['name'],
# })
headers = {'Content-Type': 'application/json'}
# 调用写入宿主机接口
# url_dir = 'http://192.168.50.202:8000/create_and_write_file'
url_dir = config['model_parser_url'] + '/create_and_write_file'
data = {
'folder_path': new_folder_path, # 您想要创建的文件夹路径
'file_path': new_file_path, # 您想要创建的文件名
'content': item['data'] # 您想要写入文件的内容
}
requests.post(url_dir, json=data, headers=headers)
# 调用特征包接口
url = config['model_parser_url'] + '/process_file'
payload = {
'file_path': new_file_path,
'dest_path': new_folder_path,
'back_url': config['bfm_url']
}
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
print("Request was successful.")
print("Response: ", response.json())
else:
print("Request failed.")
# 特征识别
xml_path = new_folder_path + '/' + item.name.rpartition('.')[0] + '_FeatrueTable.XML'
print('xml_path', xml_path)
parser_obj = pc.FeatureParser(xml_path)
print('parser_obj', parser_obj)
slot = parser_obj.slots
print('slot', slot)
hole = parser_obj.holes
print('hole', hole)
size = parser_obj.size
print('size', size)
open_slot = parser_obj.open_slots
print('open_slot', open_slot)
vector = parser_obj.vectors
print('vector', vector)
print('all parcer', size)
try:
hole_time = pc.hole_time(parser_obj)
print('hole_time', hole_time)
except Exception as e:
return json.dumps({'code': 400, 'msg': '孔尺寸超限', 'error_msg': str(e)})
try:
slot_time = pc.slot_time(parser_obj)
print('slot_time', slot_time)
except Exception as e:
return json.dumps({'code': 400, 'msg': '槽尺寸超限', 'error_msg': str(e)})
try:
open_slot_time = pc.open_slot_time(parser_obj)
print('open_slot_time', open_slot_time)
except Exception as e:
return json.dumps({'code': 400, 'msg': '开口槽尺寸超限', 'error_msg': str(e)})
total_time = hole_time + slot_time + open_slot_time
print(hole_time, slot_time, open_slot_time)
print('total_time', total_time)
ret = {'feature_infos': [{'name': 'all_feature', 'type': '', 'process_time': total_time}],
'boxshape': size, 'slugX': 10.0, 'slugY': 90.0, 'slugZ': 42.0,
'turn_over_times': 2,
'target_faces': ['A', 'B']}
self.model_feature = json.dumps(ret['feature_infos'], ensure_ascii=False)
self.model_length = size['length'] # 长 单位mm
self.model_width = size['width'] # 宽
self.model_height = size['height'] # 高
self.model_volume = size['length'] * size['width'] * size['height']
# 附件处理
base64_data = base64.b64encode(item.datas)
base64_datas = base64_data.decode('utf-8')
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# 读取文件
shapes = read_step_file(new_file_path)
output_file = os.path.join(new_folder_path, str(model_code) + '.stl')
write_stl_file(shapes, output_file, 'binary', 0.03, 0.5)
# 转化为glb
output_glb_file = os.path.join(new_folder_path, str(model_code) + '.glb')
util_path = get_resource_path('jikimo_gateway_api', 'static/util')
# 根据操作系统确定使用 'python' 还是 'python3'
python_cmd = 'python3' if os_name == 'Linux' else 'python'
print('python_cmd', python_cmd)
print('os_name', os_name)
# 使用引号包围路径
cmd = '%s "%s/stl2gltf.py" "%s" "%s" -b' % (python_cmd, util_path, output_file, output_glb_file)
logging.info(cmd)
os.system(cmd)
# 转base64
with open(output_glb_file, 'rb') as fileObj:
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
return base64_data
except Exception as e:
return UserError('模型自动报价失败,请联系管理员')
# 将attach的datas内容转为glb文件
def transition_glb_file(self, report_path, model_code):
shapes = read_step_file(report_path)
@@ -116,24 +259,7 @@ class QuickEasyOrder(models.Model):
raise ValidationError('只允许上传一个文件')
if item.upload_model_file:
file_attachment_id = item.upload_model_file[0]
# 附件路径
report_path = file_attachment_id._full_path(file_attachment_id.store_fname)
logging.info("模型路径: %s" % report_path)
base64_data = base64.b64encode(file_attachment_id.datas)
base64_datas = base64_data.decode('utf-8')
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
logging.info("模型编码: %s" % model_code)
item.model_file = self.transition_glb_file(report_path, model_code)
ret = self.feature_recognition(report_path, model_code)
logging.info("自动报价返回值: %s" % ret)
boxshape = ret['boxshape'].tolist()
logging.info("自动报价boxshape: %s" % boxshape)
logging.info('自动报价feature_infos:%s' % ret['feature_infos'])
item.model_length = boxshape[0] # 长 单位mm
item.model_width = boxshape[1] # 宽
item.model_height = boxshape[2] # 高
item.model_volume = boxshape[0] * boxshape[1] * boxshape[2]
item.model_feature = json.dumps(ret['feature_infos'], ensure_ascii=False)
item.model_file = self.model_analyze(file_attachment_id)
self._get_price(item)
else:
item.model_file = False

View File

@@ -1,5 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_quick_easy_order,quick_easy_order,model_quick_easy_order,base.group_system,1,1,1,1
access_quick_easy_order,quick_easy_order,model_quick_easy_order,base.group_system,1,1,1,0
access_quick_easy_order_group_sale_salemanager,quick_easy_order_group_sale_salemanager,model_quick_easy_order,sf_base.group_sale_salemanager,1,1,1,0
access_quick_easy_order_group_sale_director,quick_easy_order_group_sale_director,model_quick_easy_order,sf_base.group_sale_director,1,1,1,0
access_sf_auto_quatotion_common,sf_auto_quatotion_common,model_sf_auto_quatotion_common,base.group_system,1,1,1,1
access_sale_order_manager,sale_order_manager,model_sale_order,sf_base.group_sale_salemanager,1,1,1,0
access_sale_order_director,sale_order_director,model_sale_order,sf_base.group_sale_director,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_quick_easy_order quick_easy_order model_quick_easy_order base.group_system 1 1 1 1 0
3 access_quick_easy_order_group_sale_salemanager quick_easy_order_group_sale_salemanager model_quick_easy_order sf_base.group_sale_salemanager 1 1 1 0
4 access_quick_easy_order_group_sale_director quick_easy_order_group_sale_director model_quick_easy_order sf_base.group_sale_director 1 1 1 0
5 access_sf_auto_quatotion_common sf_auto_quatotion_common model_sf_auto_quatotion_common base.group_system 1 1 1 1
6 access_sale_order_manager sale_order_manager model_sale_order sf_base.group_sale_salemanager 1 1 1 0
7 access_sale_order_director sale_order_director model_sale_order sf_base.group_sale_director 1 1 1 0

View File

@@ -47,7 +47,7 @@
</h1>
<group>
<group>
<field name="customer_id" readonly="1" force_save="1"/>
<field name="customer_id" />
<field name="material_id"/>
<field name="material_model_id"/>
<!-- <field name="process_id"/>-->