对模型进行上色和特征识别及自动报价
This commit is contained in:
@@ -13,6 +13,7 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
token = fields.Char(string='TOKEN', default='b811ac06-3f00-11ed-9aed-0242ac110003')
|
token = fields.Char(string='TOKEN', default='b811ac06-3f00-11ed-9aed-0242ac110003')
|
||||||
sf_secret_key = fields.Char(string='密钥', default='wBmxej38OkErKhD6')
|
sf_secret_key = fields.Char(string='密钥', default='wBmxej38OkErKhD6')
|
||||||
sf_url = fields.Char(string='访问地址', default='https://sf.cs.jikimo.com')
|
sf_url = fields.Char(string='访问地址', default='https://sf.cs.jikimo.com')
|
||||||
|
bfm_url = fields.Char(string='业务平台后端访问地址', default='https://bfm.jikimo.com')
|
||||||
ftp_host = fields.Char(string='FTP的ip')
|
ftp_host = fields.Char(string='FTP的ip')
|
||||||
ftp_port = fields.Char(string='FTP端口')
|
ftp_port = fields.Char(string='FTP端口')
|
||||||
ftp_user = fields.Char(string='FTP用户')
|
ftp_user = fields.Char(string='FTP用户')
|
||||||
@@ -81,6 +82,7 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
token = config.get_param('token', default='')
|
token = config.get_param('token', default='')
|
||||||
sf_secret_key = config.get_param('sf_secret_key', default='')
|
sf_secret_key = config.get_param('sf_secret_key', default='')
|
||||||
sf_url = config.get_param('sf_url', default='')
|
sf_url = config.get_param('sf_url', default='')
|
||||||
|
bfm_url = config.get_param('bfm_url', default='')
|
||||||
ftp_host = config.get_param('ftp_host', default='')
|
ftp_host = config.get_param('ftp_host', default='')
|
||||||
ftp_port = config.get_param('ftp_port', default='')
|
ftp_port = config.get_param('ftp_port', default='')
|
||||||
ftp_user = config.get_param('ftp_user', default='')
|
ftp_user = config.get_param('ftp_user', default='')
|
||||||
@@ -90,6 +92,7 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
token=token,
|
token=token,
|
||||||
sf_secret_key=sf_secret_key,
|
sf_secret_key=sf_secret_key,
|
||||||
sf_url=sf_url,
|
sf_url=sf_url,
|
||||||
|
bfm_url=bfm_url,
|
||||||
ftp_host=ftp_host,
|
ftp_host=ftp_host,
|
||||||
ftp_port=ftp_port,
|
ftp_port=ftp_port,
|
||||||
ftp_user=ftp_user,
|
ftp_user=ftp_user,
|
||||||
@@ -103,6 +106,7 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
ir_config.set_param("token", self.token or "")
|
ir_config.set_param("token", self.token or "")
|
||||||
ir_config.set_param("sf_secret_key", self.sf_secret_key or "")
|
ir_config.set_param("sf_secret_key", self.sf_secret_key or "")
|
||||||
ir_config.set_param("sf_url", self.sf_url or "")
|
ir_config.set_param("sf_url", self.sf_url or "")
|
||||||
|
ir_config.set_param("bfm_url", self.bfm_url or "")
|
||||||
ir_config.set_param("ftp_host", self.ftp_host or "")
|
ir_config.set_param("ftp_host", self.ftp_host or "")
|
||||||
ir_config.set_param("ftp_port", self.ftp_port or "")
|
ir_config.set_param("ftp_port", self.ftp_port or "")
|
||||||
ir_config.set_param("ftp_user", self.ftp_user or "")
|
ir_config.set_param("ftp_user", self.ftp_user or "")
|
||||||
|
|||||||
@@ -60,6 +60,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>业务平台参数配置</h2>
|
||||||
|
<div class="row mt16 o_settings_container" id="pay_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" string="访问地址"/>
|
||||||
|
<field name="bfm_url"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
37
sf_sale/models/auto_quatotion_common.py
Normal file
37
sf_sale/models/auto_quatotion_common.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from odoo.modules import get_resource_path
|
||||||
|
from odoo import fields, models, api
|
||||||
|
from quatotion import readSql, feature_recognize, auto_quatotion
|
||||||
|
|
||||||
|
__author__ = 'jinling.yang'
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AutoQuatotion(models.Model):
|
||||||
|
_name = 'sf.auto_quatotion.common'
|
||||||
|
_description = u'自动报价公用类'
|
||||||
|
|
||||||
|
# 获取feature.sqlite的地址
|
||||||
|
def get_feature_full_path(self):
|
||||||
|
return get_resource_path('sf_sale', 'models', 'feature.sqlite')
|
||||||
|
|
||||||
|
# 获取price.sqlite的地址
|
||||||
|
def get_price_full_path(self):
|
||||||
|
return get_resource_path('sf_sale', 'models', 'price.sqlite')
|
||||||
|
|
||||||
|
# 获取price.sqlite的地址
|
||||||
|
def get_process_time_db_path(self):
|
||||||
|
return get_resource_path('sf_sale', 'models', 'process_time.db')
|
||||||
|
|
||||||
|
def get_auto_quatotion(self, stp_url, feature_full_path, process_time_db_path, model_code):
|
||||||
|
'''
|
||||||
|
通过打包好的.so库,
|
||||||
|
以调用autoQuatotion库中Quatotion类,
|
||||||
|
初始化后调用类的analyseShape方法对模型文件进行价格预测
|
||||||
|
'''
|
||||||
|
# 初始化自动报价类(输入特征数据库和加工时间数据库)
|
||||||
|
reader = auto_quatotion.Quatotion(feature_full_path, process_time_db_path)
|
||||||
|
# 获取价格、加工时间、尺寸、XYZ、翻面次数
|
||||||
|
feature_info = reader.analyseShape(stp_url, InfoJson={})
|
||||||
|
return feature_info
|
||||||
BIN
sf_sale/models/feature.sqlite
Normal file
BIN
sf_sale/models/feature.sqlite
Normal file
Binary file not shown.
BIN
sf_sale/models/price.sqlite
Normal file
BIN
sf_sale/models/price.sqlite
Normal file
Binary file not shown.
BIN
sf_sale/models/process_time.db
Normal file
BIN
sf_sale/models/process_time.db
Normal file
Binary file not shown.
@@ -18,11 +18,12 @@ class QuickEasyOrder(models.Model):
|
|||||||
_description = '简易下单'
|
_description = '简易下单'
|
||||||
|
|
||||||
name = fields.Char('订单编号', default=lambda self: self.env['ir.sequence'].next_by_code('quick.easy.order'))
|
name = fields.Char('订单编号', default=lambda self: self.env['ir.sequence'].next_by_code('quick.easy.order'))
|
||||||
model_length = fields.Float('长[mm]', digits=(16, 3))
|
model_length = fields.Float('长(mm)', digits=(16, 3))
|
||||||
model_width = fields.Float('宽[mm]', digits=(16, 3))
|
model_width = fields.Float('宽(mm)', digits=(16, 3))
|
||||||
model_height = fields.Float('高[mm]', digits=(16, 3))
|
model_height = fields.Float('高(mm)', digits=(16, 3))
|
||||||
model_volume = fields.Float('体积[mm³]', digits=(16, 3))
|
model_volume = fields.Float('体积(mm³)', digits=(16, 3))
|
||||||
model_processing_side = fields.Char('加工面')
|
model_processing_side = fields.Char('加工面', default='A')
|
||||||
|
model_feature = fields.Char('特征')
|
||||||
machining_precision = fields.Selection([
|
machining_precision = fields.Selection([
|
||||||
('0.10', '±0.10mm'),
|
('0.10', '±0.10mm'),
|
||||||
('0.05', '±0.05mm'),
|
('0.05', '±0.05mm'),
|
||||||
@@ -44,6 +45,9 @@ class QuickEasyOrder(models.Model):
|
|||||||
('待接单', '待接单'), ('加工中', '加工中'),
|
('待接单', '待接单'), ('加工中', '加工中'),
|
||||||
('物流中', '物流中'), ('已交付', '已交付')], string='订单状态', default='草稿',
|
('物流中', '物流中'), ('已交付', '已交付')], string='订单状态', default='草稿',
|
||||||
readonly=True)
|
readonly=True)
|
||||||
|
model_color_state = fields.Selection([
|
||||||
|
('success', '成功'),
|
||||||
|
('fail', '失败')], string='模型上色状态')
|
||||||
|
|
||||||
@api.depends('material_id', 'material_model_id')
|
@api.depends('material_id', 'material_model_id')
|
||||||
def _compute_material_model(self):
|
def _compute_material_model(self):
|
||||||
@@ -69,23 +73,9 @@ class QuickEasyOrder(models.Model):
|
|||||||
report_path = attachment._full_path(attachment.store_fname)
|
report_path = attachment._full_path(attachment.store_fname)
|
||||||
vals['model_file'] = self.transition_glb_file(report_path, model_code)
|
vals['model_file'] = self.transition_glb_file(report_path, model_code)
|
||||||
logging.info('create-model_file:%s' % len(vals['model_file']))
|
logging.info('create-model_file:%s' % len(vals['model_file']))
|
||||||
feature_path = self.env['jikimo.auto_quatotion.common'].sudo().get_feature_full_path()
|
|
||||||
# price_path = self.env['jikimo.auto_quatotion.common'].get_price_full_path()
|
|
||||||
process_time_db_path = self.env['jikimo.auto_quatotion.common'].sudo().get_process_time_db_path()
|
|
||||||
ret = self.env['jikimo.auto_quatotion.common'].sudo().get_auto_quatotion(report_path, feature_path,
|
|
||||||
process_time_db_path,
|
|
||||||
model_code)
|
|
||||||
logging.info("自动报价返回值: %s" % ret)
|
|
||||||
shapes = ret['boxshape'].tolist()
|
|
||||||
logging.info("自动报价boxshape: %s" % shapes)
|
|
||||||
target_faces = ','.join(ret['target_faces']),
|
|
||||||
# item.price = ret['price']
|
|
||||||
item.model_length = shapes[0] # 长 单位mm
|
|
||||||
item.model_width = shapes[1] # 宽
|
|
||||||
item.model_height = shapes[2] # 高
|
|
||||||
item.model_volume = shapes[0] * shapes[1] * shapes[2]
|
|
||||||
item.model_processing_side = target_faces[0]
|
|
||||||
obj = super(QuickEasyOrder, self).create(vals)
|
obj = super(QuickEasyOrder, self).create(vals)
|
||||||
|
self.model_coloring()
|
||||||
self.distribute_to_factory(obj)
|
self.distribute_to_factory(obj)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@@ -123,15 +113,32 @@ class QuickEasyOrder(models.Model):
|
|||||||
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
|
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
|
||||||
logging.info("模型编码: %s" % model_code)
|
logging.info("模型编码: %s" % model_code)
|
||||||
item.model_file = self.transition_glb_file(report_path, 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)
|
||||||
|
self._get_price(item)
|
||||||
else:
|
else:
|
||||||
item.model_file = False
|
item.model_file = False
|
||||||
|
item.model_feature = False
|
||||||
|
item.model_length = 0
|
||||||
|
item.model_width = 0
|
||||||
|
item.model_height = 0
|
||||||
|
item.model_volume = 0
|
||||||
|
|
||||||
def distribute_to_factory(self, obj):
|
def distribute_to_factory(self, obj):
|
||||||
"""
|
"""
|
||||||
派单到工厂
|
派单到工厂
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
web_base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url', default=''),
|
web_base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url', default='')
|
||||||
|
logging.info("自动报价返回值: %s" % ret)
|
||||||
url = '/api/bfm_process_order/list'
|
url = '/api/bfm_process_order/list'
|
||||||
res = {'order_number': obj.name, 'delivery_end_date': str(datetime.now()),
|
res = {'order_number': obj.name, 'delivery_end_date': str(datetime.now()),
|
||||||
'delivery_name': 'XXXXX', 'delivery_telephone': 'XXXXX',
|
'delivery_name': 'XXXXX', 'delivery_telephone': 'XXXXX',
|
||||||
@@ -181,3 +188,94 @@ class QuickEasyOrder(models.Model):
|
|||||||
raise UserError(e)
|
raise UserError(e)
|
||||||
else:
|
else:
|
||||||
raise UserError("分配工厂失败,请联系管理员")
|
raise UserError("分配工厂失败,请联系管理员")
|
||||||
|
|
||||||
|
# 特征识别
|
||||||
|
def feature_recognition(self, report_path, model_code):
|
||||||
|
feature_path = self.env['sf.auto_quatotion.common'].sudo().get_feature_full_path()
|
||||||
|
# price_path = self.env['jikimo.auto_quatotion.common'].get_price_full_path()
|
||||||
|
process_time_db_path = self.env['sf.auto_quatotion.common'].sudo().get_process_time_db_path()
|
||||||
|
ret = self.env['sf.auto_quatotion.common'].sudo().get_auto_quatotion(report_path, feature_path,
|
||||||
|
process_time_db_path,
|
||||||
|
model_code)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
# 模型上色
|
||||||
|
def model_coloring(self):
|
||||||
|
url = '/api/library_of_models/create'
|
||||||
|
config = self.env['res.config.settings'].get_values()
|
||||||
|
config_header = Common.get_headers(self, config['sf_token'], config['sf_key_secret'])
|
||||||
|
order = self.search([('id', '=', self.id)])
|
||||||
|
logging.info('order: %s' % order.name)
|
||||||
|
if order:
|
||||||
|
attachment = order.upload_model_file[0]
|
||||||
|
base64_data = base64.b64encode(attachment.datas)
|
||||||
|
base64_datas = base64_data.decode('utf-8')
|
||||||
|
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
|
||||||
|
logging.info('model_file-size: %s' % len(order.model_file))
|
||||||
|
logging.info('attachment.datas-size: %s' % len(attachment.datas))
|
||||||
|
vals = {
|
||||||
|
'model_code': model_code,
|
||||||
|
'model_data': base64_data,
|
||||||
|
'model_name': attachment.name,
|
||||||
|
'model_long': order.model_length,
|
||||||
|
'model_width': order.model_width,
|
||||||
|
'model_height': order.model_height,
|
||||||
|
'model_volume': order.model_volume,
|
||||||
|
'model_order_no': order.name,
|
||||||
|
'remark': '订单号:%s 客户:%s' % (order.name, order.customer_id.name)
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
ret = requests.post((config['sf_url'] + url), json={}, data=vals, headers=config_header,
|
||||||
|
timeout=60)
|
||||||
|
ret = ret.json()
|
||||||
|
# result = json.loads(ret['result'])
|
||||||
|
if ret['status'] == 1:
|
||||||
|
order.model_color_state = 'success'
|
||||||
|
else:
|
||||||
|
order.model_color_state = 'fail'
|
||||||
|
raise UserError(ret['message'])
|
||||||
|
except Exception as e:
|
||||||
|
order.model_color_state = 'fail'
|
||||||
|
raise UserError("模型上色失败")
|
||||||
|
|
||||||
|
# 自动报价
|
||||||
|
def _get_price(self, order):
|
||||||
|
|
||||||
|
url = '/api/automatic_quotes'
|
||||||
|
config = self.env['res.config.settings'].sudo().get_values()
|
||||||
|
config_header = Common.get_headers(self, config['sf_token'], config['sf_key_secret'])
|
||||||
|
logging.info("报价接口..........% s" % order.name)
|
||||||
|
try:
|
||||||
|
if order:
|
||||||
|
vals = {}
|
||||||
|
# mrs合作伙伴token
|
||||||
|
vals['token'] = config['sf_token']
|
||||||
|
vals['accuracy'] = order.machining_precision
|
||||||
|
vals['number'] = order.quantity
|
||||||
|
vals['process_code'] = 0
|
||||||
|
vals['texture_code'] = order.material_model_id.code
|
||||||
|
vals['delivery_days'] = 15
|
||||||
|
if order.model_file:
|
||||||
|
attachment = self.env['ir.attachment'].sudo().search(
|
||||||
|
[('id', '=', order.upload_model_file[0])])
|
||||||
|
vals['attachment_id'] = attachment.id
|
||||||
|
else:
|
||||||
|
vals['attachment_id'] = ''
|
||||||
|
vals['feature_infos'] = order.model_feature
|
||||||
|
vals['model_long'] = order.model_length
|
||||||
|
vals['model_width'] = order.model_width
|
||||||
|
vals['model_height'] = order.model_height
|
||||||
|
logging.info('vals:%s' % vals)
|
||||||
|
ret = requests.post((config['sf_url'] + url), json={}, data=vals, headers=config_header)
|
||||||
|
result = json.dumps(json.loads(ret.text), ensure_ascii=False, indent=4, separators=(',', ':'))
|
||||||
|
logging.info('报价接口返回:%s' % result)
|
||||||
|
price_result = json.loads(result)
|
||||||
|
# random.randint(0, 10000)
|
||||||
|
order.write({'price': price_result.get('price')})
|
||||||
|
else:
|
||||||
|
raise UserError("订单不存在")
|
||||||
|
except Exception as e:
|
||||||
|
if ret['status'] != 1:
|
||||||
|
raise UserError(e)
|
||||||
|
else:
|
||||||
|
raise UserError("自动报价失败,请联系管理员")
|
||||||
|
|||||||
@@ -52,6 +52,21 @@
|
|||||||
<field name="upload_model_file" widget="many2many_binary"/>
|
<field name="upload_model_file" widget="many2many_binary"/>
|
||||||
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
|
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
|
||||||
attrs="{'invisible': [('model_file', '=', False)]}"/>
|
attrs="{'invisible': [('model_file', '=', False)]}"/>
|
||||||
|
<label for="model_length" string="尺寸(mm)"
|
||||||
|
attrs='{"invisible": [("model_file","=",False)]}'/>
|
||||||
|
<div class="test_model"
|
||||||
|
attrs='{"invisible": [("model_file","=",False)]}'>
|
||||||
|
<label for="model_length" string="长"/>
|
||||||
|
<field name="model_length" class="o_address_zip"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
<label for="model_width" string="宽"/>
|
||||||
|
<field name="model_width" class="o_address_zip"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
<label for="model_height" string="高"/>
|
||||||
|
<field name="model_height" class="o_address_zip"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
</div>
|
||||||
|
<field name="model_volume" attrs="{'invisible': [('model_file', '=', False)]}"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
@@ -80,7 +95,7 @@
|
|||||||
<field name="context">{}</field>
|
<field name="context">{}</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
[便捷订单] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
[快速订单] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
</p>
|
</p>
|
||||||
@@ -91,9 +106,9 @@
|
|||||||
<!-- action="account.res_partner_action_customer"-->
|
<!-- action="account.res_partner_action_customer"-->
|
||||||
<!-- groups="sales_team.group_sale_salesman"-->
|
<!-- groups="sales_team.group_sale_salesman"-->
|
||||||
<!-- sequence="40"/>-->
|
<!-- sequence="40"/>-->
|
||||||
<menuitem sequence="50" name="上传模型" id="menu_quick_easy_order"
|
<menuitem sequence="21" name="快速订单" id="menu_quick_easy_order"
|
||||||
action="action_quick_easy_order"
|
action="action_quick_easy_order"
|
||||||
parent="sale.menu_sale_order"
|
parent="sale.sale_order_menu"
|
||||||
groups="sales_team.group_sale_salesman"/>
|
groups="sales_team.group_sale_salesman"/>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
Reference in New Issue
Block a user