diff --git a/sf_base/models/tool_base_new.py b/sf_base/models/tool_base_new.py index 98eea36c..3517766d 100644 --- a/sf_base/models/tool_base_new.py +++ b/sf_base/models/tool_base_new.py @@ -321,7 +321,7 @@ class ToolInventory(models.Model): prefix = fields.Char('前缀') postfix = fields.Char('后缀') diameter = fields.Float('直径(mm)') - angle = fields.Float('R角(mm)') + angle = fields.Float('R角(mm)',default=0) tool_length = fields.Float('刀具总长(mm)') blade_length = fields.Float('避空长/刃长(mm)') knife_head_name = fields.Char('刀头名称') diff --git a/sf_base/models/tool_other_features.py b/sf_base/models/tool_other_features.py index bc8f369e..3876f39a 100644 --- a/sf_base/models/tool_other_features.py +++ b/sf_base/models/tool_other_features.py @@ -23,7 +23,7 @@ class ToolMaterialsBasicParameters(models.Model): handle_length = fields.Float('柄部长度(mm)') blade_tip_diameter = fields.Float('刀尖直径(mm)') blade_tip_working_size = fields.Char('刀尖倒角度(°)', size=20) - tip_r_size = fields.Float('刀尖R角(mm)') + tip_r_size = fields.Float('刀尖R角(mm)',default=0) blade_tip_taper = fields.Integer('刀尖锥度(°)') blade_diameter = fields.Float('刃部直径(mm)') blade_length = fields.Float('刃部长度(mm)') diff --git a/sf_machine_connect/__manifest__.py b/sf_machine_connect/__manifest__.py index ac8aef64..db7ae467 100644 --- a/sf_machine_connect/__manifest__.py +++ b/sf_machine_connect/__manifest__.py @@ -30,7 +30,6 @@ 'views/machine_info_present.xml', 'views/delivery_record.xml', 'views/res_config_settings_views.xml', - 'views/maintenance_views.xml', ], 'assets': { diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 1f65c884..b8c31cb6 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -162,7 +162,7 @@ class Sf_Dashboard_Connect(http.Controller): # 关机时长:初次上线时间 - 通电时间 'power_off_time': power_off_time, # 关机率:关机时长/初次上线时间 - 'power_off_rate': power_off_rate, + # 'power_off_rate': power_off_rate, 'first_online_duration': first_online_duration, # 停机时间:关机时间 - 运行时间 # 停机时长:关机时间 - 初次上线时间 @@ -187,11 +187,11 @@ class Sf_Dashboard_Connect(http.Controller): """ res = {'status': 1, 'message': '成功', 'data': {}} logging.info('前端请求日志数据的参数为:%s' % kw) + # 连接数据库 + conn = psycopg2.connect(**db_config) + cur = conn.cursor() try: - # 连接数据库 - 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('"') @@ -231,6 +231,12 @@ class Sf_Dashboard_Connect(http.Controller): res['message'] = '前端请求日志数据失败,原因:%s' % e return json.dumps(res) + finally: + if cur: + cur.close() + if conn: + conn.close() + @http.route('/api/logs/page_data', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") def logs_page_data(self, **kw): @@ -242,11 +248,10 @@ class Sf_Dashboard_Connect(http.Controller): res = {'status': 1, 'message': '成功', 'data': {}} logging.info('前端请求日志数据的参数为:%s' % kw) + # 连接数据库 + conn = psycopg2.connect(**db_config) + cur = conn.cursor() try: - # 连接数据库 - conn = psycopg2.connect(**db_config) - cur = conn.cursor() - # 获取并解析传递的参数 machine_list = ast.literal_eval(kw.get('machine_list', '[]')) begin_time_str = kw.get('begin_time', '').strip('"') @@ -848,10 +853,10 @@ class Sf_Dashboard_Connect(http.Controller): ''' sql2 = ''' - SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time + SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time FROM device_data - WHERE device_name = %s AND alarm_time IS NOT NULL - ORDER BY alarm_time, time; + WHERE device_name = %s AND alarm_start_time IS NOT NULL + ORDER BY alarm_start_time, time; ''' # 执行SQL命令 @@ -867,8 +872,11 @@ class Sf_Dashboard_Connect(http.Controller): 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])) + alarm_count.append(row[1]) + if row[0]: + total_alarm_time += abs(float(row[0])) + else: + total_alarm_time += 0.0 if len(list(set(alarm_count))) == 1: if list(set(alarm_count))[0] is None: alarm_count_num = 0 @@ -1233,17 +1241,24 @@ class Sf_Dashboard_Connect(http.Controller): machine_list = ast.literal_eval(kw['machine_list']) time_threshold = datetime.now() - timedelta(days=1) + alarm_last_24_time = 0.0 + 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 + # 获取当前时间的时间戳 + current_timestamp = datetime.now().timestamp() for item in machine_list: + euipment_obj = request.env['maintenance.equipment'].sudo().search([('code', '=', item)]) + # 机床上线时间段 + first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp() with conn.cursor() as cur: cur.execute(""" SELECT * FROM device_data WHERE device_name = %s - AND device_state != '离线' + AND device_state != '离线' AND process_time IS NOT NULL ORDER BY time DESC LIMIT 1; """, (item,)) @@ -1252,18 +1267,52 @@ class Sf_Dashboard_Connect(http.Controller): cur.execute(""" SELECT * FROM device_data WHERE device_name = %s - AND device_state != '离线' AND time >= %s + AND device_state != '离线' AND time >= %s AND process_time IS NOT NULL ORDER BY time ASC LIMIT 1; """, (item, time_threshold)) last_24_time = fetch_result_as_dict(cur) + + with conn.cursor() as cur: + cur.execute(""" + 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 AND time >= %s + ORDER BY idle_start_time, time + ) subquery; + """, (item, time_threshold)) + idle_count = cur.fetchone()[0] + + alarm_last_24_nums = [] + with conn.cursor() as cur: + cur.execute(""" + SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time + FROM device_data + WHERE device_name = %s + AND alarm_start_time IS NOT NULL AND time >= %s; + """, (item, time_threshold)) + results = cur.fetchall() + for result in results: + alarm_last_24_nums.append(result[1]) + if result[0]: + if float(result[0]) >= 1000: + continue + alarm_last_24_time += float(result[0]) + else: + alarm_last_24_time += 0.0 # 返回数据 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, 'cut_24_time': last_24_time['process_time'] if last_24_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, - 'power_on_24_time': last_24_time['power_on_time'] if last_24_time['power_on_time'] is not None else 0 + 'power_on_24_time': last_24_time['power_on_time'] if last_24_time['power_on_time'] is not None else 0, + 'alarm_last_24_time': alarm_last_24_time, + 'alarm_last_24_nums': len(list(set(alarm_last_24_nums))), + 'idle_count': idle_count, + 'first_online_time': first_online_duration, } conn.close() diff --git a/sf_machine_connect/models/ftp_client.py b/sf_machine_connect/models/ftp_client.py index 1d56424f..b5c99a7a 100644 --- a/sf_machine_connect/models/ftp_client.py +++ b/sf_machine_connect/models/ftp_client.py @@ -285,26 +285,6 @@ class Machine_ftp(models.Model): # # 开动率 run_rate = fields.Char('开动率', readonly=True) - # 同步CNC设备到oee - def sync_oee(self): - """ - 同步CNC设备到oee - :return: - """ - for record in self: - record.ensure_one() - cnc_oee_dict = { - 'equipment_id': record.id, - 'type_id': record.type_id.id, - 'machine_tool_picture': record.machine_tool_picture, - 'equipment_code': record.code, - 'function_type': record.function_type, - } - if self.env['maintenance.equipment.oee.logs'].search([('equipment_id', '=', record.id)]): - self.env['maintenance.equipment.oee.logs'].write(cnc_oee_dict) - else: - self.env['maintenance.equipment.oee.logs'].create(cnc_oee_dict) - class WorkCenterBarcode(models.Model): """ diff --git a/sf_machine_connect/views/machine_monitor.xml b/sf_machine_connect/views/machine_monitor.xml index 5a05778f..20d3f1f6 100644 --- a/sf_machine_connect/views/machine_monitor.xml +++ b/sf_machine_connect/views/machine_monitor.xml @@ -14,7 +14,7 @@ - + diff --git a/sf_machine_connect/views/maintenance_views.xml b/sf_machine_connect/views/maintenance_views.xml deleted file mode 100644 index 82c5aff4..00000000 --- a/sf_machine_connect/views/maintenance_views.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - sf.machine.hr.equipment.view.tree.inherit - maintenance.equipment - - - -
-
-
-
-
- -
\ No newline at end of file diff --git a/sf_maintenance/models/sf_maintenance_oee.py b/sf_maintenance/models/sf_maintenance_oee.py index ee2c209c..0c75ba53 100644 --- a/sf_maintenance/models/sf_maintenance_oee.py +++ b/sf_maintenance/models/sf_maintenance_oee.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import re import json import datetime import requests @@ -6,6 +7,42 @@ from odoo import api, fields, models, _ from odoo.exceptions import UserError +def convert_to_seconds(time_str): + # 修改正则表达式,使 H、M、S 部分可选 + + if time_str is None: + return 0 + + pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?" + + match = re.match(pattern, time_str) + + if match: + # 提取各时间单位,如果某个单位缺失则默认设为0 + hours = int(match.group(1)) if match.group(1) else 0 + minutes = int(match.group(2)) if match.group(2) else 0 + seconds = int(match.group(3)) if match.group(3) else 0 + + # 计算总秒数 + total_seconds = hours * 3600 + minutes * 60 + seconds + if total_seconds == 0: + # return None + pattern = r"(?:(\d+)小时)?(?:(\d+)分钟)?(?:(\d+)秒)?" + match = re.match(pattern, time_str) + if match: + # 提取各时间单位,如果某个单位缺失则默认设为0 + hours = int(match.group(1)) if match.group(1) else 0 + minutes = int(match.group(2)) if match.group(2) else 0 + seconds = int(match.group(3)) if match.group(3) else 0 + + # 计算总秒数 + total_seconds = hours * 3600 + minutes * 60 + seconds + return total_seconds + else: + return None + return total_seconds + + class SfMaintenanceEquipmentOEE(models.Model): _name = 'maintenance.equipment.oee' _description = '设备OEE' @@ -22,18 +59,27 @@ class SfMaintenanceEquipmentOEE(models.Model): [("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")], default='正常', string="机床状态", related='equipment_id.state') - run_time = fields.Float('加工时长(h)') - equipment_time = fields.Float('开机时长(h)') - done_nums = fields.Integer('加工件数') - utilization_rate = fields.Char('可用率') - fault_time = fields.Float('故障时长') - fault_nums = fields.Integer('故障次数') - # 故障率 - fault_rate = fields.Char('故障率') + + online_time = fields.Char('开机时长(小时)', reaonly='True') + + offline_time = fields.Char('关机时长(小时)', reaonly='True') + idle_nums = fields.Integer('待机次数', reaonly='True') + # 待机时长 + + idle_time = fields.Char('待机时长(小时)', reaonly='True') + + # 待机率 + idle_rate = fields.Char('待机率(%)', reaonly='True') + + work_time = fields.Char('加工时长(小时)', reaonly='True') + work_rate = fields.Char('可用率(%)', reaonly='True') + fault_time = fields.Char('故障时长(小时)', reaonly='True') + fault_rate = fields.Char('故障率(%)', reaonly='True') + fault_nums = fields.Integer('故障次数', reaonly='True') + # 设备故障日志 sf_maintenance_logs_ids = fields.One2many('sf.maintenance.logs', 'maintenance_equipment_oee_id', '设备故障日志', related='equipment_id.sf_maintenance_logs_ids') - oee_logs = fields.One2many('maintenance.equipment.oee.logs', 'equipment_oee_id', string='运行日志') day_logs_detail = fields.Html('日运行日志详情') history_logs_detail = fields.Html('历史运行日志详情') @@ -42,8 +88,13 @@ class SfMaintenanceEquipmentOEE(models.Model): # 获取日志详情 def get_day_logs(self): + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + print(base_url) config = self.env['ir.config_parameter'].sudo() - url = 'http://172.16.10.112:8069/api/logs/list' + # url = 'http://172.16.10.112:8069/api/logs/list' + # url_time = 'http://localhost:9069/api/RunningTimeDetail' + url = base_url + '/api/logs/list' + url_time = base_url + '/api/RunningTimeDetail' machine_list = [self.equipment_code] begin_time = datetime.datetime.now().strftime('%Y-%m-%d') + ' 00:00:00' end_time = datetime.datetime.now().strftime('%Y-%m-%d') + ' 23:59:59' @@ -55,11 +106,41 @@ class SfMaintenanceEquipmentOEE(models.Model): "end_time": end_time } + data_time = { + "machine_list": str(machine_list) + } + print(data) # 发送POST请求 response = requests.post(url, json={}, data=data) - print(response.json()) # 输出服务器返回的响应 + response_time = requests.post(url_time, json={}, data=data_time) + # print(response.json()) # 输出服务器返回的响应 + print(response_time.json()) + if response_time.status_code == 200: + result_time = response_time.json() + real_dict = result_time['data'][self.equipment_code] + print('=', result_time) + if result_time['status'] == 1: + self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds( + real_dict['power_on_24_time'])) / 3600, 2) + self.work_time = round( + (convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600, + 2) + self.offline_time = 24 - (float(self.online_time) if self.online_time else 0) + self.idle_time = float(self.online_time) - float( + self.work_time) if self.online_time and self.work_time else 0 + self.idle_rate = round( + float(self.idle_time) / (float(self.online_time) if self.online_time else 1) * 100, 2) + self.work_rate = round( + float(self.work_time) / (float(self.online_time) if self.online_time else 1) * 100, 2) + self.fault_time = (float(real_dict['alarm_last_24_time']) if real_dict[ + 'alarm_last_24_time'] else 0) / 3600 + self.fault_rate = round( + float(self.fault_time) / (float(self.online_time) if self.online_time else 1) * 100, 2) + self.fault_nums = real_dict['alarm_last_24_nums'] + self.idle_nums = real_dict['idle_count'] + if response.status_code == 200: result = response.json() print('============', result) @@ -94,7 +175,10 @@ class SfMaintenanceEquipmentOEE(models.Model): # 获取历史日志详情 def get_history_logs(self): config = self.env['ir.config_parameter'].sudo() - url = 'http://172.16.10.112:8069/api/logs/list' + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + # url = 'http://172.16.10.112:8069/api/logs/list' + url = base_url + '/api/logs/list' + url_time = base_url + '/api/RunningTimeDetail' machine_list = [self.equipment_code] if not self.begin_time: raise UserError('请选择开始时间') diff --git a/sf_maintenance/security/ir.model.access.csv b/sf_maintenance/security/ir.model.access.csv index ae18b36f..abbd4878 100644 --- a/sf_maintenance/security/ir.model.access.csv +++ b/sf_maintenance/security/ir.model.access.csv @@ -67,6 +67,3 @@ access_sf_cutting_tool_type_admin_sf_group_equipment_user,sf_cutting_tool_type_a access_sf_cutting_tool_type_group_purchase_director_sf_group_equipment_user,sf_cutting_tool_type_group_purchase_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0 access_sf_cutting_tool_type_group_sale_director_sf_group_equipment_user,sf_cutting_tool_type_group_sale_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0 access_sf_cutting_tool_type_group_plan_director_sf_group_equipment_user,sf_cutting_tool_type_group_plan_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0 - -access_maintenance_equipment_oee_logs,maintenance_equipment_oee_logs,model_maintenance_equipment_oee_logs,sf_maintenance.sf_group_equipment_manager,1,1,1,1 -access_maintenance_equipment_oee_log_detail,maintenance_equipment_oee_log_detail,model_maintenance_equipment_oee_log_detail,sf_maintenance.sf_group_equipment_manager,1,1,1,1 \ No newline at end of file diff --git a/sf_maintenance/views/maintenance_equipment_oee_views.xml b/sf_maintenance/views/maintenance_equipment_oee_views.xml index b798b0ab..98d6dec5 100644 --- a/sf_maintenance/views/maintenance_equipment_oee_views.xml +++ b/sf_maintenance/views/maintenance_equipment_oee_views.xml @@ -8,13 +8,13 @@ - - - - + + + + @@ -37,19 +37,48 @@ - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -81,28 +110,28 @@ - - - - - - - - - - - - -