diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 94c0fdcb..96708dd8 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -173,49 +173,6 @@ class Sf_Dashboard_Connect(http.Controller): res['message'] = '前端请求机床数据失败,原因:%s' % e return json.JSONEncoder().encode(res) - # @http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, - # cors="*") - # def logs_list(self, **kw): - # """ - # 拿到日志数据返回给大屏展示 - # :param kw: - # :return: - # """ - # res = {'status': 1, 'message': '成功', 'data': []} - # logging.info('前端请求日志数据的参数为:%s' % kw) - # - # try: - # # 获取请求的日志数据 - # logs_obj = request.env['maintenance.equipment.oee.log.detail'].sudo() - # # 获取请求的机床数据 - # machine_list = ast.literal_eval(kw['machine_list']) - # begin_time_str = kw['begin_time'].strip('"') - # end_time_str = kw['end_time'].strip('"') - # - # 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') - # - # print('begin_time: %s' % begin_time) - # for item in machine_list: - # log_datas = logs_obj.search( - # [('equipment_code', '=', item), ('time', '>=', begin_time), ('time', '<=', end_time)]) - # print('log_datas: %s' % log_datas) - # for log_data in log_datas: - # res['data'].append({ - # 'equipment_code': log_data.equipment_code, - # 'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'), - # 'state': log_data.state - # - # }) - # - # return json.JSONEncoder().encode(res) - # - # except Exception as e: - # logging.info('前端请求日志数据失败,原因:%s' % e) - # res['status'] = -1 - # res['message'] = '前端请求日志数据失败,原因:%s' % e - # return json.JSONEncoder().encode(res) - @http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") def logs_list(self, **kw): """ @@ -227,9 +184,9 @@ class Sf_Dashboard_Connect(http.Controller): logging.info('前端请求日志数据的参数为:%s' % kw) try: - # 获取请求的日志数据 - logs_obj = request.env['maintenance.equipment.oee.log.detail'].sudo() - # 获取请求的机床数据 + # 连接数据库 + conn = psycopg2.connect(**db_config) + cur = conn.cursor() machine_list = ast.literal_eval(kw['machine_list']) begin_time_str = kw['begin_time'].strip('"') end_time_str = kw['end_time'].strip('"') @@ -240,19 +197,25 @@ class Sf_Dashboard_Connect(http.Controller): print('begin_time: %s' % begin_time) for item in machine_list: - log_datas = logs_obj.search( - [('equipment_code', '=', item), ('time', '>=', begin_time), ('time', '<=', end_time)]) - print('log_datas: %s' % log_datas) + sql = ''' + SELECT time, device_state, program_name + FROM device_data + WHERE device_name = %s AND time >= %s AND time <= %s + ORDER BY time ASC; + ''' + # 执行SQL命令,使用参数绑定 + cur.execute(sql, (item, begin_time, end_time)) + results = cur.fetchall() # 将数据按照 equipment_code 进行分组 if item not in res['data']: res['data'][item] = [] - for log_data in log_datas: + for result in results: res['data'][item].append({ - 'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'), - 'state': log_data.state, - 'production_name': log_data.production_name, + 'time': result[0].strftime('%Y-%m-%d %H:%M:%S'), + 'state': result[1], + 'production_name': result[2], }) return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode() @@ -624,7 +587,7 @@ class Sf_Dashboard_Connect(http.Controller): # 查询pg库来获得待机次数 @http.route('/api/IdleAlarmCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") - def idle_count(self, **kw): + def idle_alarm_count(self, **kw): """ 查询设备的待机次数 """ @@ -637,69 +600,50 @@ class Sf_Dashboard_Connect(http.Controller): try: # 获取请求的机床数据 machine_list = ast.literal_eval(kw['machine_list']) - idle_times = [] - idle_dict = {} - + total_alarm_time = 0 + alarm_count_num = 0 for item in machine_list: sql = ''' - SELECT idle_start_time,alarm_time,alarm_repair_time FROM device_data WHERE device_name = %s; + SELECT COUNT(*) + FROM ( + SELECT DISTINCT ON (idle_start_time) idle_start_time + FROM device_data + WHERE device_name = %s AND idle_start_time IS NOT NULL + ORDER BY idle_start_time, time + ) subquery; + ''' + + sql2 = ''' + SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time + FROM device_data + WHERE device_name = %s AND alarm_time IS NOT NULL + ORDER BY alarm_time, time; + ''' # 执行SQL命令 cur.execute(sql, (item,)) result = cur.fetchall() - # # print('result', result) - # - # # 将查询结果添加到idle_times列表中 - # idle_times = [row[0] for row in result if row[0] is not None] - # - # # 对结果去重 - # unique_idle_times = set(idle_times) - # # print('unique_idle_times', unique_idle_times) - # - # # 统计去重后的数量 - # idle_count = len(unique_idle_times) - # # idle_dict[item] = idle_count - # - # res['data'][item] = idle_count - - total_alarm_time = 0 - alarm_count = 0 - alarm_time_list = [] - idle_times = [] - alarm_times = [] + print('result========', result) + cur.execute(sql2, (item,)) + result2 = cur.fetchall() + print('result2========', result2) + # for row in result: - idle_start_time = row[0] - alarm_time = row[1] - alarm_repair_time = row[2] - - alarm_time_list.append(alarm_time) # 将时长累加,以秒为单位 - idle_times.append(idle_start_time) - # if alarm_repair_time is not None: - # alarm_times.append(alarm_repair_time) - alarm_times.append(alarm_repair_time) - - # 对结果去重 - unique_total_alarm_time = set(alarm_time_list) - unique_idle_times = set(idle_times) - unique_alarm_times = set(alarm_times) - - # 统计去重后的数量 - idle_count = len(unique_idle_times) - - for alarm_time in unique_total_alarm_time: - if alarm_time is not None: - total_alarm_time += abs(float(alarm_time)) - - alarm_count = len(unique_alarm_times) if unique_alarm_times else 0 - alarm_count = alarm_count if total_alarm_time else 0 - - # 存储待机次数和总待机时长(单位:秒) - res['data'][item] = { - 'idle_count': idle_count, - 'total_alarm_time': total_alarm_time / 3600, # 以秒为单位 - 'alarm_count': alarm_count - } + res['data'][item] = {'idle_count': row[0]} + alarm_count = [] + for row in result2: + alarm_count.append(row[0]) + total_alarm_time += abs(float(row[0])) + if len(list(set(alarm_count))) == 1: + if list(set(alarm_count))[0] is None: + alarm_count_num = 0 + else: + alarm_count_num = 1 + else: + alarm_count_num = len(list(set(alarm_count))) + res['data'][item]['total_alarm_time'] = total_alarm_time / 3600 + res['data'][item]['alarm_count_num'] = alarm_count_num # 返回统计结果 return json.dumps(res) @@ -712,7 +656,7 @@ class Sf_Dashboard_Connect(http.Controller): # 查询pg库来获得异常情况 @http.route('/api/alarm/logs', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") - def idle_count(self, **kw): + def alarm_logs(self, **kw): """ 查询设备的异常情况 """ @@ -764,3 +708,110 @@ class Sf_Dashboard_Connect(http.Controller): finally: cur.close() conn.close() + + # 设备oee + @http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") + def OEE(self, **kw): + """ + 获取产线等oee + """ + res = {'status': 1, 'message': '成功', 'data': {}} + logging.info('前端请求oee数据的参数为:%s' % kw) + + try: + count_oee = 1 + workcenter_obj = request.env['mrp.workcenter'].sudo() + workcenter_list = ast.literal_eval(kw['workcenter_list']) + print('workcenter_list: %s' % workcenter_list) + for line in workcenter_list: + res['data'][line] = workcenter_obj.search([('name', '=', line)]).oee + count_oee *= workcenter_obj.search([('name', '=', line)]).oee + res['data']['综合oee'] = count_oee / 1000000 + except Exception as e: + print(f"An error occurred: {e}") + + return json.dumps(res) + + # # 查询某段时间的设备oee + # @http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") + # def OEEByTime(self, **kw): + # """ + # 获取某段时间的oee + # """ + # res = {'status': 1, 'message': '成功', 'data': {}} + # logging.info('前端请求获取某段时间的oee的参数为:%s' % kw) + # workcenter_list = ast.literal_eval(kw['workcenter_list']) + # begin_time_str = kw['begin_time'].strip('"') + # end_time_str = kw['end_time'].strip('"') + # 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') + # print('workcenter_list: %s' % workcenter_list) + # # 连接数据库 + # conn = psycopg2.connect(**db_config) + # cur = conn.cursor() + # # 查询并计算OEE平均值 + # oee_data = {} + # for workcenter in workcenter_list: + # cur.execute(""" + # SELECT AVG(oee) as avg_oee + # FROM oee_data + # WHERE workcenter_name = %s + # AND time BETWEEN %s AND %s + # """, (workcenter, begin_time, end_time)) + # + # result = cur.fetchone() + # avg_oee = result[0] if result else 0.0 + # oee_data[workcenter] = avg_oee + # + # # 返回数据 + # res['data'] = oee_data + # return json.dumps(res) + + @http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") + def OEEByTime(self, **kw): + """ + 获取某段时间的oee,根据用户指定的时间单位(day或hour)返回对应的平均值 + """ + res = {'status': 1, 'message': '成功', 'data': {}} + logging.info('前端请求获取某段时间的oee的参数为:%s' % kw) + + # 获取并解析参数 + workcenter_list = ast.literal_eval(kw['workcenter_list']) + begin_time_str = kw['begin_time'].strip('"') + end_time_str = kw['end_time'].strip('"') + time_unit = kw.get('time_unit', 'day') # 默认单位为天 + 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') + + # 连接数据库 + conn = psycopg2.connect(**db_config) + cur = conn.cursor() + + # 根据时间单位选择不同的时间格式 + if time_unit == 'hour': + time_format = 'YYYY-MM-DD HH24:00:00' + else: # 默认为'day' + time_format = 'YYYY-MM-DD' + + # 查询并计算OEE平均值 + oee_data = {} + for workcenter in workcenter_list: + cur.execute(f""" + SELECT to_char(time, '{time_format}') as time_unit, AVG(oee) as avg_oee + FROM oee_data + WHERE workcenter_name = %s + AND time BETWEEN %s AND %s + GROUP BY time_unit + ORDER BY time_unit + """, (workcenter, begin_time, end_time)) + + results = cur.fetchall() + oee_data[workcenter] = {row[0]: row[1] for row in results} + + # 关闭数据库连接 + cur.close() + conn.close() + + # 返回数据 + res['data'] = oee_data + return json.dumps(res) diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 461ef6ad..03597980 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -41,6 +41,7 @@ class ResWorkcenter(models.Model): oee_target = fields.Float( string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True) + oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', store=True) time_start = fields.Float('Setup Time', tracking=True) time_stop = fields.Float('Cleanup Time', tracking=True) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index e6a190fa..96237924 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1159,10 +1159,10 @@ class ResMrpWorkOrder(models.Model): def button_finish(self): for record in self: if record.routing_type == '装夹预调': - if not record.material_center_point or record.X_deviation_angle <= 0: - raise UserError("请对前置三元检测定位参数进行计算定位") if not record.rfid_code and record.is_rework is False: raise UserError("请扫RFID码进行绑定") + if not record.material_center_point or record.X_deviation_angle <= 0: + raise UserError("请对前置三元检测定位参数进行计算定位") record.process_state = '待加工' # record.write({'process_state': '待加工'}) record.production_id.process_state = '待加工'