Accept Merge Request #1301: (feature/优化制造功能 -> develop)
Merge Request: 优化dashboard接口,及装夹bug Created By: @马广威 Accepted By: @马广威 URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1301?initial=true
This commit is contained in:
@@ -6,7 +6,7 @@ import base64
|
|||||||
import logging
|
import logging
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from odoo import http
|
from odoo import http, fields
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
# 数据库连接配置
|
# 数据库连接配置
|
||||||
@@ -15,14 +15,18 @@ db_config = {
|
|||||||
"user": "postgres",
|
"user": "postgres",
|
||||||
"password": "postgres",
|
"password": "postgres",
|
||||||
"port": "5432",
|
"port": "5432",
|
||||||
"host": "172.16.10.98"
|
"host": "172.16.10.113"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def convert_to_seconds(time_str):
|
def convert_to_seconds(time_str):
|
||||||
# 修改正则表达式,使 H、M、S 部分可选
|
# 修改正则表达式,使 H、M、S 部分可选
|
||||||
|
|
||||||
|
if time_str is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
match = re.match(pattern, time_str)
|
match = re.match(pattern, time_str)
|
||||||
|
|
||||||
if match:
|
if match:
|
||||||
@@ -66,7 +70,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
# 获取当前时间的时间戳
|
# 获取当前时间的时间戳
|
||||||
current_timestamp = datetime.now().timestamp()
|
current_timestamp = datetime.now().timestamp()
|
||||||
print(current_timestamp)
|
# print(current_timestamp)
|
||||||
|
|
||||||
# tem_list = [
|
# tem_list = [
|
||||||
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
||||||
@@ -162,7 +166,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'first_online_duration': first_online_duration,
|
'first_online_duration': first_online_duration,
|
||||||
# 停机时间:关机时间 - 运行时间
|
# 停机时间:关机时间 - 运行时间
|
||||||
# 停机时长:关机时间 - 初次上线时间
|
# 停机时长:关机时间 - 初次上线时间
|
||||||
'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
|
# 'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
|
||||||
|
'img': f'https://xt.sf.jikimo.com/equipment/get_image/{machine_data.id}',
|
||||||
'equipment_type': machine_data.category_id.name,
|
'equipment_type': machine_data.category_id.name,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -194,7 +199,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
print('begin_time: %s' % begin_time)
|
# print('begin_time: %s' % begin_time)
|
||||||
|
|
||||||
for item in machine_list:
|
for item in machine_list:
|
||||||
sql = '''
|
sql = '''
|
||||||
@@ -275,7 +280,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
res = {'Succeed': True}
|
res = {'Succeed': True}
|
||||||
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
||||||
line_list = list(map(lambda x: x.name, line_list_obj))
|
line_list = list(map(lambda x: x.name, line_list_obj))
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
res['LineList'] = line_list
|
res['LineList'] = line_list
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -300,21 +305,40 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
plan_obj = request.env['sf.production.plan'].sudo()
|
plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
|
production_obj = request.env['mrp.production'].sudo()
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
plan_data = plan_obj.search([('production_line_id.name', '=', line)])
|
|
||||||
# 工单总量
|
# 工单计划量
|
||||||
plan_data_total_counts = plan_obj.search_count([('production_line_id.name', '=', line)])
|
plan_data_total_counts = production_obj.search_count(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
||||||
|
('active', '=', True)])
|
||||||
# 工单完成量
|
# 工单完成量
|
||||||
plan_data_finish_counts = plan_obj.search_count(
|
plan_data_finish_counts = plan_obj.search_count(
|
||||||
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
# 工单计划量
|
# # 工单计划量
|
||||||
plan_data_plan_counts = plan_obj.search_count(
|
# plan_data_plan_counts = plan_obj.search_count(
|
||||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
||||||
# 工单不良累计
|
|
||||||
plan_data_fault_counts = plan_obj.search_count(
|
# 查找符合条件的生产计划记录
|
||||||
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel'])])
|
plan_data = plan_obj.search([
|
||||||
|
('production_line_id.name', '=', line),
|
||||||
|
])
|
||||||
|
|
||||||
|
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
|
||||||
|
faulty_plans = plan_data.filtered(lambda p: any(
|
||||||
|
result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
|
||||||
|
))
|
||||||
|
|
||||||
|
# 查找制造订单取消与归档的数量
|
||||||
|
cancel_order_count = production_obj.search_count(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'in', ['cancel']),
|
||||||
|
('active', '=', False)])
|
||||||
|
|
||||||
|
# 计算符合条件的记录数量
|
||||||
|
# plan_data_fault_counts = len(faulty_plans) + cancel_order_count
|
||||||
|
plan_data_fault_counts = len(faulty_plans)
|
||||||
|
|
||||||
# 工单返工数量
|
# 工单返工数量
|
||||||
|
|
||||||
@@ -326,17 +350,76 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
(plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3)
|
(plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3)
|
||||||
|
|
||||||
# 工单进度偏差
|
# 工单进度偏差
|
||||||
plan_data_progress_deviation = plan_data_finish_counts - plan_data_plan_counts
|
plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
|
||||||
|
|
||||||
|
# 完成记录
|
||||||
|
plan_data_finish_orders = plan_obj.search(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
|
|
||||||
|
# # 检测量
|
||||||
|
# detection_nums = 0
|
||||||
|
# for i in plan_data_finish_orders:
|
||||||
|
# print('i: %s' % i)
|
||||||
|
# if i.production_id.detection_result_ids:
|
||||||
|
# detection_nums += 1
|
||||||
|
#
|
||||||
|
# print('detection_nums: %s' % detection_nums)
|
||||||
|
|
||||||
|
# 检测量
|
||||||
|
detection_data = len(
|
||||||
|
plan_data_finish_orders.mapped('production_id.detection_result_ids').filtered(lambda r: r))
|
||||||
|
|
||||||
|
# 检测合格量
|
||||||
|
pass_nums = plan_data_finish_orders.filtered(lambda p: any(
|
||||||
|
result.test_results in ['合格'] for result in p.production_id.detection_result_ids
|
||||||
|
))
|
||||||
|
|
||||||
|
# 质量合格率
|
||||||
|
pass_rate = 1
|
||||||
|
if pass_nums:
|
||||||
|
pass_rate = round(
|
||||||
|
(len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
|
||||||
|
|
||||||
|
# 返工率
|
||||||
|
rework_rate = round(
|
||||||
|
(plan_data_rework_counts / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
|
||||||
|
|
||||||
|
# 交付准时率
|
||||||
|
delay_num = 0
|
||||||
|
for plan in plan_data_finish_orders:
|
||||||
|
sale_obj = request.env['sale.order'].sudo().search([('name', '=', plan.origin)])
|
||||||
|
if sale_obj:
|
||||||
|
if sale_obj.deadline_of_delivery and plan.actual_end_time:
|
||||||
|
# 将 deadline_of_delivery 转换为字符串
|
||||||
|
date_as_string = sale_obj.deadline_of_delivery.strftime('%Y-%m-%d')
|
||||||
|
# 然后使用 strptime 将字符串转换为 datetime 对象
|
||||||
|
date_as_datetime = datetime.strptime(date_as_string, '%Y-%m-%d')
|
||||||
|
|
||||||
|
# 将 actual_end_time 转换为 datetime 对象
|
||||||
|
datetime_value = fields.Datetime.from_string(plan.actual_end_time)
|
||||||
|
|
||||||
|
# 给 datetime_value 加1天
|
||||||
|
new_datetime_value = datetime_value + timedelta(days=1)
|
||||||
|
|
||||||
|
# 比较 new_datetime_value 和 date_as_datetime 的大小
|
||||||
|
if new_datetime_value.date() > date_as_datetime.date():
|
||||||
|
delay_num += 1
|
||||||
|
delay_rate = round((delay_num / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
|
||||||
|
on_time_rate = 1 - delay_rate
|
||||||
|
|
||||||
if plan_data:
|
if plan_data:
|
||||||
data = {
|
data = {
|
||||||
'plan_data_total_counts': plan_data_total_counts,
|
'plan_data_total_counts': plan_data_total_counts,
|
||||||
'plan_data_finish_counts': plan_data_finish_counts,
|
'plan_data_finish_counts': plan_data_finish_counts,
|
||||||
'plan_data_plan_counts': plan_data_plan_counts,
|
'plan_data_plan_counts': plan_data_total_counts,
|
||||||
'plan_data_fault_counts': plan_data_fault_counts,
|
'plan_data_fault_counts': plan_data_fault_counts,
|
||||||
|
'nopass_orders_counts': detection_data - len(pass_nums),
|
||||||
'finishe_rate': finishe_rate,
|
'finishe_rate': finishe_rate,
|
||||||
'plan_data_progress_deviation': plan_data_progress_deviation,
|
'plan_data_progress_deviation': plan_data_progress_deviation,
|
||||||
'plan_data_rework_counts': plan_data_rework_counts
|
'plan_data_rework_counts': plan_data_rework_counts,
|
||||||
|
'on_time_rate': on_time_rate,
|
||||||
|
'detection_data': detection_data,
|
||||||
|
'pass_rate': pass_rate
|
||||||
}
|
}
|
||||||
res['data'][line] = data
|
res['data'][line] = data
|
||||||
|
|
||||||
@@ -363,7 +446,28 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
|
print('kw', kw)
|
||||||
|
time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天
|
||||||
|
print('time_unit: %s' % time_unit)
|
||||||
|
|
||||||
|
# 日期或小时循环生成器,根据time_unit决定是按天还是按小时
|
||||||
|
def get_time_intervals(start_time, end_time, time_unit):
|
||||||
|
intervals = []
|
||||||
|
current_time = start_time
|
||||||
|
if time_unit == 'hour':
|
||||||
|
delta = timedelta(hours=1)
|
||||||
|
else:
|
||||||
|
delta = timedelta(days=1)
|
||||||
|
|
||||||
|
while current_time < end_time:
|
||||||
|
next_time = current_time + delta
|
||||||
|
# 确保最后一个时间段不会超出end_time
|
||||||
|
if next_time > end_time:
|
||||||
|
next_time = end_time
|
||||||
|
intervals.append((current_time, next_time))
|
||||||
|
current_time = next_time
|
||||||
|
return intervals
|
||||||
|
|
||||||
def get_date_list(start_date, end_date):
|
def get_date_list(start_date, end_date):
|
||||||
date_list = []
|
date_list = []
|
||||||
@@ -374,10 +478,33 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
return date_list
|
return date_list
|
||||||
|
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
date_list = get_date_list(begin_time, end_time)
|
date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
|
||||||
order_counts = []
|
order_counts = []
|
||||||
|
|
||||||
date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
|
if time_unit == 'hour':
|
||||||
|
time_intervals = get_time_intervals(begin_time, end_time, time_unit)
|
||||||
|
print('============================= %s' % time_intervals)
|
||||||
|
|
||||||
|
time_count_dict = {}
|
||||||
|
|
||||||
|
for time_interval in time_intervals:
|
||||||
|
start_time, end_time = time_interval
|
||||||
|
# print(start_time, end_time)
|
||||||
|
orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||||
|
(date_field_name, '>=', start_time.strftime('%Y-%m-%d 00:00:00')),
|
||||||
|
(date_field_name, '<', end_time.strftime('%Y-%m-%d 00:00:00'))
|
||||||
|
])
|
||||||
|
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
||||||
|
key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
|
||||||
|
time_count_dict[key] = len(orders)
|
||||||
|
# order_counts.append()
|
||||||
|
res['data'][line] = {
|
||||||
|
'finish_order_nums': time_count_dict,
|
||||||
|
'plan_order_nums': 28
|
||||||
|
}
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
date_list = get_date_list(begin_time, end_time)
|
||||||
|
|
||||||
for date in date_list:
|
for date in date_list:
|
||||||
next_day = date + timedelta(days=1)
|
next_day = date + timedelta(days=1)
|
||||||
@@ -487,7 +614,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
not_done_data = []
|
not_done_data = []
|
||||||
done_data = []
|
done_data = []
|
||||||
final_data = {}
|
final_data = {}
|
||||||
@@ -495,11 +622,16 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
for line in line_list:
|
for line in line_list:
|
||||||
# 未完成订单
|
# 未完成订单
|
||||||
not_done_orders = plan_obj.search(
|
not_done_orders = plan_obj.search(
|
||||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
[('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
||||||
print(not_done_orders)
|
('production_id.state', 'not in', ['cancel']), ('active', '=', True)
|
||||||
|
])
|
||||||
|
# print(not_done_orders)
|
||||||
# 完成订单
|
# 完成订单
|
||||||
finish_orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
finish_orders = plan_obj.search([
|
||||||
print(finish_orders)
|
('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||||
|
('production_id.state', 'not in', ['cancel']), ('active', '=', True)
|
||||||
|
])
|
||||||
|
# print(finish_orders)
|
||||||
|
|
||||||
# 获取所有未完成订单的ID列表
|
# 获取所有未完成订单的ID列表
|
||||||
order_ids = [order.id for order in not_done_orders]
|
order_ids = [order.id for order in not_done_orders]
|
||||||
@@ -622,11 +754,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# 执行SQL命令
|
# 执行SQL命令
|
||||||
cur.execute(sql, (item,))
|
cur.execute(sql, (item,))
|
||||||
result = cur.fetchall()
|
result = cur.fetchall()
|
||||||
print('result========', result)
|
# print('result========', result)
|
||||||
|
|
||||||
cur.execute(sql2, (item,))
|
cur.execute(sql2, (item,))
|
||||||
result2 = cur.fetchall()
|
result2 = cur.fetchall()
|
||||||
print('result2========', result2)
|
# print('result2========', result2)
|
||||||
#
|
#
|
||||||
for row in result:
|
for row in result:
|
||||||
res['data'][item] = {'idle_count': row[0]}
|
res['data'][item] = {'idle_count': row[0]}
|
||||||
@@ -682,7 +814,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# 执行SQL命令
|
# 执行SQL命令
|
||||||
cur.execute(sql)
|
cur.execute(sql)
|
||||||
result = cur.fetchall()
|
result = cur.fetchall()
|
||||||
print('result', result)
|
# print('result', result)
|
||||||
|
|
||||||
# 将查询结果转换为字典列表
|
# 将查询结果转换为字典列表
|
||||||
data = []
|
data = []
|
||||||
@@ -830,3 +962,222 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# 返回数据
|
# 返回数据
|
||||||
res['data'] = oee_data
|
res['data'] = oee_data
|
||||||
return json.dumps(res)
|
return json.dumps(res)
|
||||||
|
|
||||||
|
@http.route(['/equipment/get_image/<int:record_id>'], type='http', auth="public", website=True)
|
||||||
|
def get_image(self, record_id, **kwargs):
|
||||||
|
# 获取模型中的记录
|
||||||
|
record = request.env['maintenance.equipment'].sudo().browse(record_id)
|
||||||
|
|
||||||
|
# 获取图片字段的数据
|
||||||
|
image_data_base64 = record.machine_tool_picture
|
||||||
|
|
||||||
|
if image_data_base64:
|
||||||
|
# 将Base64解码为二进制数据
|
||||||
|
image_data_binary = base64.b64decode(image_data_base64)
|
||||||
|
|
||||||
|
# 返回图片数据,并设置正确的Content-Type
|
||||||
|
return request.make_response(image_data_binary, headers=[('Content-Type', 'image/png')])
|
||||||
|
else:
|
||||||
|
# 如果没有图片数据,返回404
|
||||||
|
return request.not_found()
|
||||||
|
|
||||||
|
# 设备运行率
|
||||||
|
@http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
|
def RunningTime(self, **kw):
|
||||||
|
"""
|
||||||
|
获取设备运行时长
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
|
# 连接数据库
|
||||||
|
conn = psycopg2.connect(**db_config)
|
||||||
|
# 获取请求的机床数据
|
||||||
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
|
|
||||||
|
def fetch_result_as_dict(cursor):
|
||||||
|
"""辅助函数:将查询结果转为字典"""
|
||||||
|
columns = [desc[0] for desc in cursor.description]
|
||||||
|
return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
|
||||||
|
|
||||||
|
# 初始化当天、当月和有记录以来的总时长
|
||||||
|
day_total_running_time = 0
|
||||||
|
day_total_process_time = 0
|
||||||
|
day_work_rate = 0
|
||||||
|
month_total_running_time = 0
|
||||||
|
month_total_process_time = 0
|
||||||
|
month_work_rate = 0
|
||||||
|
all_time_total_running_time = 0
|
||||||
|
all_time_total_process_time = 0
|
||||||
|
all_time_work_rate = 0
|
||||||
|
|
||||||
|
for item in machine_list:
|
||||||
|
# 获取当天第一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND time::date = CURRENT_DATE
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
first_today = fetch_result_as_dict(cur)
|
||||||
|
# print("当天第一条记录(非离线):", first_today)
|
||||||
|
|
||||||
|
# 获取当天最新一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND time::date = CURRENT_DATE
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_today = fetch_result_as_dict(cur)
|
||||||
|
# print("当天最新一条记录(非离线):", last_today)
|
||||||
|
|
||||||
|
# 计算当天运行时长
|
||||||
|
if first_today and last_today:
|
||||||
|
running_time = convert_to_seconds(last_today['run_time']) - convert_to_seconds(first_today['run_time'])
|
||||||
|
process_time = convert_to_seconds(last_today['process_time']) - convert_to_seconds(
|
||||||
|
first_today['process_time'])
|
||||||
|
day_total_running_time += abs(running_time)
|
||||||
|
day_total_process_time += abs(process_time)
|
||||||
|
|
||||||
|
# 获取当月第一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND EXTRACT(YEAR FROM time) = EXTRACT(YEAR FROM CURRENT_DATE)
|
||||||
|
AND EXTRACT(MONTH FROM time) = EXTRACT(MONTH FROM CURRENT_DATE)
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
first_month = fetch_result_as_dict(cur)
|
||||||
|
# print("当月第一条记录(非离线):", first_month)
|
||||||
|
|
||||||
|
# 获取当月最新一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND EXTRACT(YEAR FROM time) = EXTRACT(YEAR FROM CURRENT_DATE)
|
||||||
|
AND EXTRACT(MONTH FROM time) = EXTRACT(MONTH FROM CURRENT_DATE)
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_month = fetch_result_as_dict(cur)
|
||||||
|
# print("当月最新一条记录(非离线):", last_month)
|
||||||
|
|
||||||
|
# 计算当月运行时长
|
||||||
|
if first_month and last_month:
|
||||||
|
month_running_time = convert_to_seconds(last_month['run_time']) - convert_to_seconds(
|
||||||
|
first_month['run_time'])
|
||||||
|
month_process_time = convert_to_seconds(last_month['process_time']) - convert_to_seconds(
|
||||||
|
first_month['process_time'])
|
||||||
|
month_total_running_time += abs(month_running_time)
|
||||||
|
month_total_process_time += abs(month_process_time)
|
||||||
|
|
||||||
|
# 获取有记录以来的第一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
first_all_time = fetch_result_as_dict(cur)
|
||||||
|
# print("有记录以来的第一条记录(非离线):", first_all_time)
|
||||||
|
|
||||||
|
# 获取有记录以来的最新一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_all_time = fetch_result_as_dict(cur)
|
||||||
|
# print("有记录以来的最新一条记录(非离线):", last_all_time)
|
||||||
|
|
||||||
|
# 计算有记录以来的运行时长
|
||||||
|
if first_all_time and last_all_time:
|
||||||
|
all_time_running_time = convert_to_seconds(last_all_time['run_time']) - convert_to_seconds(
|
||||||
|
first_all_time['run_time'])
|
||||||
|
all_time_process_time = convert_to_seconds(last_all_time['process_time']) - convert_to_seconds(
|
||||||
|
first_all_time['process_time'])
|
||||||
|
all_time_total_running_time += abs(all_time_running_time)
|
||||||
|
all_time_total_process_time += abs(all_time_process_time)
|
||||||
|
|
||||||
|
# 计算当天工作效率
|
||||||
|
if day_total_running_time > day_total_process_time:
|
||||||
|
day_work_rate = day_total_process_time / day_total_running_time if day_total_running_time != 0 else 0
|
||||||
|
else:
|
||||||
|
day_work_rate = day_total_running_time / day_total_process_time if day_total_process_time != 0 else 0
|
||||||
|
print("当天工作效率: %s" % day_work_rate)
|
||||||
|
|
||||||
|
# 计算当月工作效率
|
||||||
|
if month_total_running_time > month_total_process_time:
|
||||||
|
month_work_rate = month_total_process_time / month_total_running_time if month_total_running_time != 0 else 0
|
||||||
|
else:
|
||||||
|
month_work_rate = month_total_running_time / month_total_process_time if month_total_process_time != 0 else 0
|
||||||
|
print("当月工作效率: %s" % month_work_rate)
|
||||||
|
|
||||||
|
# 计算有记录以来的工作效率
|
||||||
|
if all_time_total_running_time > all_time_total_process_time:
|
||||||
|
all_time_work_rate = all_time_total_process_time / all_time_total_running_time if all_time_total_running_time != 0 else 0
|
||||||
|
else:
|
||||||
|
all_time_work_rate = all_time_total_running_time / all_time_total_process_time if all_time_total_process_time != 0 else 0
|
||||||
|
print("有记录以来的工作效率: %s" % all_time_work_rate)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# 返回数据
|
||||||
|
res['data']['day_work_rate'] = day_work_rate
|
||||||
|
res['data']['month_work_rate'] = month_work_rate
|
||||||
|
res['data']['all_time_work_rate'] = all_time_work_rate
|
||||||
|
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
# 设备运行时长
|
||||||
|
@http.route('/api/RunningTimeDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
|
def RunningTimeDetail(self, **kw):
|
||||||
|
"""
|
||||||
|
获取
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
|
# 连接数据库
|
||||||
|
conn = psycopg2.connect(**db_config)
|
||||||
|
# 获取请求的机床数据
|
||||||
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
|
|
||||||
|
def fetch_result_as_dict(cursor):
|
||||||
|
"""辅助函数:将查询结果转为字典"""
|
||||||
|
columns = [desc[0] for desc in cursor.description]
|
||||||
|
return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
|
||||||
|
|
||||||
|
for item in machine_list:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_all_time = fetch_result_as_dict(cur)
|
||||||
|
# 返回数据
|
||||||
|
res['data'][item] = {
|
||||||
|
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
||||||
|
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
|
||||||
|
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return json.dumps(res)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class ResBFMConfigSettings(models.TransientModel):
|
|||||||
# ("https://bfm.jikimo.com", "正式环境(https://bfm.jikimo.com)")], string='bfm环境', store=True)
|
# ("https://bfm.jikimo.com", "正式环境(https://bfm.jikimo.com)")], string='bfm环境', store=True)
|
||||||
|
|
||||||
bfm_url_new = fields.Char('业务平台环境路径', placeholder='请输入当前对应的业务平台环境路径')
|
bfm_url_new = fields.Char('业务平台环境路径', placeholder='请输入当前对应的业务平台环境路径')
|
||||||
|
get_check_file_path = fields.Char('获取检查文件路径', default='')
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_values(self):
|
def get_values(self):
|
||||||
@@ -26,9 +27,11 @@ class ResBFMConfigSettings(models.TransientModel):
|
|||||||
values = super(ResBFMConfigSettings, self).get_values()
|
values = super(ResBFMConfigSettings, self).get_values()
|
||||||
config = self.env['ir.config_parameter'].sudo()
|
config = self.env['ir.config_parameter'].sudo()
|
||||||
bfm_url_new = config.get_param('bfm_url_new', default='')
|
bfm_url_new = config.get_param('bfm_url_new', default='')
|
||||||
|
get_check_file_path = config.get_param('get_check_file_path', default='')
|
||||||
|
|
||||||
values.update(
|
values.update(
|
||||||
bfm_url_new=bfm_url_new,
|
bfm_url_new=bfm_url_new,
|
||||||
|
get_check_file_path=get_check_file_path
|
||||||
)
|
)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
@@ -36,3 +39,4 @@ class ResBFMConfigSettings(models.TransientModel):
|
|||||||
super(ResBFMConfigSettings, self).set_values()
|
super(ResBFMConfigSettings, self).set_values()
|
||||||
ir_config = self.env['ir.config_parameter'].sudo()
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
ir_config.set_param("bfm_url_new", self.bfm_url_new or "")
|
ir_config.set_param("bfm_url_new", self.bfm_url_new or "")
|
||||||
|
ir_config.set_param("get_check_file_path", self.get_check_file_path or "")
|
||||||
|
|||||||
@@ -18,6 +18,21 @@
|
|||||||
<field name="bfm_url_new" string="业务平台访问地址"/>
|
<field name="bfm_url_new" string="业务平台访问地址"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<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="get_check_file_path" />
|
||||||
|
<field name="get_check_file_path" string="检测报告服务地址"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -414,12 +414,10 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
|
|
||||||
# 获取三次元检测点数据
|
# 获取三次元检测点数据
|
||||||
def get_three_check_datas(self):
|
def get_three_check_datas(self):
|
||||||
factory_nick_name = 'XT'
|
|
||||||
ftp_resconfig = self.env['res.config.settings'].get_values()
|
ftp_resconfig = self.env['res.config.settings'].get_values()
|
||||||
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']),
|
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']),
|
||||||
ftp_resconfig['ftp_user'],
|
ftp_resconfig['ftp_user'],
|
||||||
ftp_resconfig['ftp_password'])
|
ftp_resconfig['ftp_password'])
|
||||||
# ftp.connect()
|
|
||||||
|
|
||||||
local_dir_path = '/ftp/before'
|
local_dir_path = '/ftp/before'
|
||||||
os.makedirs(local_dir_path, exist_ok=True)
|
os.makedirs(local_dir_path, exist_ok=True)
|
||||||
@@ -428,6 +426,17 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
logging.info('local_file_path:%s' % local_file_path)
|
logging.info('local_file_path:%s' % local_file_path)
|
||||||
remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename
|
remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename
|
||||||
logging.info('remote_path:%s' % remote_path)
|
logging.info('remote_path:%s' % remote_path)
|
||||||
|
# if not ftp.file_exists(remote_path):
|
||||||
|
paload_data = {
|
||||||
|
"filename": local_filename
|
||||||
|
}
|
||||||
|
if not ftp_resconfig['get_check_file_path']:
|
||||||
|
raise UserError('请先配置获取检测报告地址')
|
||||||
|
url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
|
||||||
|
response = requests.post(url, json=paload_data)
|
||||||
|
logging.info('response:%s' % response.json())
|
||||||
|
if response.json().get('detail'):
|
||||||
|
raise UserError(response.json().get('detail'))
|
||||||
|
|
||||||
if not ftp.file_exists(remote_path):
|
if not ftp.file_exists(remote_path):
|
||||||
raise UserError(f"文件不存在: {remote_path}")
|
raise UserError(f"文件不存在: {remote_path}")
|
||||||
@@ -540,6 +549,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
raise UserError('PT10点未测或数据错误')
|
raise UserError('PT10点未测或数据错误')
|
||||||
|
|
||||||
self.data_state = True
|
self.data_state = True
|
||||||
|
self.getcenter()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1169,7 +1179,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if not record.rfid_code and record.is_rework is False:
|
if not record.rfid_code and record.is_rework is False:
|
||||||
raise UserError("请扫RFID码进行绑定")
|
raise UserError("请扫RFID码进行绑定")
|
||||||
if record.is_rework is False:
|
if record.is_rework is False:
|
||||||
if not record.material_center_point and record.X_deviation_angle > 0:
|
if not record.material_center_point or record.X_deviation_angle <= 0:
|
||||||
raise UserError("坯料中心点为空或X偏差角度小于等于0")
|
raise UserError("坯料中心点为空或X偏差角度小于等于0")
|
||||||
record.process_state = '待加工'
|
record.process_state = '待加工'
|
||||||
# record.write({'process_state': '待加工'})
|
# record.write({'process_state': '待加工'})
|
||||||
|
|||||||
@@ -513,6 +513,11 @@
|
|||||||
|
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//form//header" position="inside">
|
||||||
|
<button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据"
|
||||||
|
attrs='{"invisible": [("state","!=","progress")]}'/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
|
||||||
<xpath expr="//page[1]" position="before">
|
<xpath expr="//page[1]" position="before">
|
||||||
<field name="results" invisible="1"/>
|
<field name="results" invisible="1"/>
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ class sf_production_plan(models.Model):
|
|||||||
('draft', '待排程'),
|
('draft', '待排程'),
|
||||||
('done', '已排程'),
|
('done', '已排程'),
|
||||||
('processing', '加工中'),
|
('processing', '加工中'),
|
||||||
('finished', '已完成')
|
('finished', '已完成'),
|
||||||
|
('cancel', '已取消')
|
||||||
], string='状态', tracking=True)
|
], string='状态', tracking=True)
|
||||||
|
|
||||||
state_order = fields.Integer(compute='_compute_state_order', store=True)
|
state_order = fields.Integer(compute='_compute_state_order', store=True)
|
||||||
@@ -37,7 +38,7 @@ class sf_production_plan(models.Model):
|
|||||||
_order = 'state_order asc, write_date desc'
|
_order = 'state_order asc, write_date desc'
|
||||||
|
|
||||||
name = fields.Char(string='制造订单')
|
name = fields.Char(string='制造订单')
|
||||||
# active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True)
|
||||||
# selected = fields.Boolean(default=False)
|
# selected = fields.Boolean(default=False)
|
||||||
# order_number = fields.Char(string='订单号')
|
# order_number = fields.Char(string='订单号')
|
||||||
order_deadline = fields.Datetime(string='订单交期')
|
order_deadline = fields.Datetime(string='订单交期')
|
||||||
@@ -385,3 +386,23 @@ class machine_work_schedule(models.Model):
|
|||||||
_description = '机台作业计划'
|
_description = '机台作业计划'
|
||||||
|
|
||||||
name = fields.Char(string='机台名')
|
name = fields.Char(string='机台名')
|
||||||
|
|
||||||
|
|
||||||
|
class MrpProductionInheritForPlan(models.Model):
|
||||||
|
_inherit = 'mrp.production'
|
||||||
|
|
||||||
|
def button_cancel(self):
|
||||||
|
# 调用父类的取消操作
|
||||||
|
res = super(MrpProductionInheritForPlan, self).button_cancel()
|
||||||
|
# 更新 sf.production.plan 模型的 state 为 'cancel'
|
||||||
|
self.env['sf.production.plan'].search([('production_id', '=', self.id)]).write({'state': 'cancel'})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def toggle_active(self):
|
||||||
|
# 调用父类方法切换 active 状态
|
||||||
|
res = super(MrpProductionInheritForPlan, self).toggle_active()
|
||||||
|
self.env['sf.production.plan'].search(
|
||||||
|
[('production_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)]).write(
|
||||||
|
{'active': self.active})
|
||||||
|
|
||||||
|
return res
|
||||||
|
|||||||
Reference in New Issue
Block a user