diff --git a/sf_machine_connect/__manifest__.py b/sf_machine_connect/__manifest__.py
index db7ae467..ac8aef64 100644
--- a/sf_machine_connect/__manifest__.py
+++ b/sf_machine_connect/__manifest__.py
@@ -30,6 +30,7 @@
'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 2fe2e7a5..945b7a86 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -1,11 +1,45 @@
# -*- coding: utf-8 -*-
+import re
import ast
import json
import logging
+from datetime import datetime
from odoo import http
from odoo.http import request
+def convert_to_seconds(time_str):
+ # 修改正则表达式,使 H、M、S 部分可选
+
+ 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 Sf_Dashboard_Connect(http.Controller):
@http.route('/api/get_machine_datas/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
@@ -18,6 +52,11 @@ class Sf_Dashboard_Connect(http.Controller):
"""
res = {'status': 1, 'message': '成功', 'data': []}
logging.info('前端请求机床数据的参数为:%s' % kw)
+
+ # 获取当前时间的时间戳
+ current_timestamp = datetime.now().timestamp()
+ print(current_timestamp)
+
# tem_list = [
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
@@ -33,8 +72,21 @@ class Sf_Dashboard_Connect(http.Controller):
machine_list = ast.literal_eval(kw['machine_list'])
for item in machine_list:
machine_data = equipment_obj.search([('code', '=', item)])
+
+ # 机床上线时间段
+ first_online_duration = current_timestamp - int(machine_data.first_online_time.timestamp())
+
+ power_off_time = None
+ power_off_rate = None
+ if machine_data.machine_power_on_time:
+ power_off_time = first_online_duration - convert_to_seconds(machine_data.machine_power_on_time)
+ power_off_rate = round((power_off_time / first_online_duration), 3)
+ else:
+ power_off_time = False
+ power_off_rate = False
if machine_data:
res['data'].append({
+ 'active': machine_data.status,
'id': machine_data.id,
'name': machine_data.name,
'brand': machine_data.type_id.name,
@@ -92,6 +144,13 @@ class Sf_Dashboard_Connect(http.Controller):
# 计算出来的数据
# 开动率:运行时间/通电时间
'run_rate': machine_data.run_rate,
+ # 关机时长:初次上线时间 - 通电时间
+ 'power_off_time': power_off_time,
+ # 关机率:关机时长/初次上线时间
+ 'power_off_rate': power_off_rate,
+ 'first_online_duration': first_online_duration,
+ # 停机时间:关机时间 - 运行时间
+ # 停机时长:关机时间 - 初次上线时间
})
return json.JSONEncoder().encode(res)
@@ -101,6 +160,95 @@ 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):
+ """
+ 拿到日志数据返回给大屏展示
+ :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)
+
+ # 将数据按照 equipment_code 进行分组
+ if item not in res['data']:
+ res['data'][item] = []
+
+ for log_data in log_datas:
+ res['data'][item].append({
+ 'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'state': log_data.state
+ })
+
+ return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
+
+ except Exception as e:
+ logging.info('前端请求日志数据失败,原因:%s' % e)
+ res['status'] = -1
+ res['message'] = '前端请求日志数据失败,原因:%s' % e
+ return json.dumps(res)
+
# 返回CNC机床列表
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
diff --git a/sf_machine_connect/models/ftp_client.py b/sf_machine_connect/models/ftp_client.py
index f2dcc826..1d56424f 100644
--- a/sf_machine_connect/models/ftp_client.py
+++ b/sf_machine_connect/models/ftp_client.py
@@ -121,6 +121,13 @@ class Machine_ftp(models.Model):
"""
_inherit = 'maintenance.equipment'
+ # 机床首次上线时间(默认取值2024年08月01日零点)
+
+ def _get_default_online_time(self):
+ return datetime(2024, 1, 1, 0, 0, 0)
+
+ first_online_time = fields.Datetime(string='首次上线时间', default=_get_default_online_time)
+
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
# # 机床配置项目
@@ -278,6 +285,26 @@ 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 c6964659..5a05778f 100644
--- a/sf_machine_connect/views/machine_monitor.xml
+++ b/sf_machine_connect/views/machine_monitor.xml
@@ -18,6 +18,7 @@
+
diff --git a/sf_machine_connect/views/maintenance_views.xml b/sf_machine_connect/views/maintenance_views.xml
new file mode 100644
index 00000000..82c5aff4
--- /dev/null
+++ b/sf_machine_connect/views/maintenance_views.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ 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 41301410..66e0a855 100644
--- a/sf_maintenance/models/sf_maintenance_oee.py
+++ b/sf_maintenance/models/sf_maintenance_oee.py
@@ -41,29 +41,32 @@ class SfMaintenanceEquipmentOEELog(models.Model):
_name = 'maintenance.equipment.oee.logs'
_description = '设备运行日志'
- equipment_id = fields.Many2one('maintenance.equipment', '机台号')
- equipment_code = fields.Char('设备编码')
+ equipment_id = fields.Many2one('maintenance.equipment', '机台号', readonly='True')
+ equipment_code = fields.Char('设备编码', readonly='True')
name = fields.Char('设备名称', readonly='True')
+ function_type = fields.Selection(
+ [("ZXJGZX", "钻铣加工中心"), ("CXJGZX", "车削加工中心"), ("FHJGZX", "复合加工中心")],
+ default="", string="功能类型")
machine_tool_picture = fields.Binary('设备图片')
- type_id = fields.Many2one('sf.machine_tool.type', '品牌型号')
+ type_id = fields.Many2one('sf.machine_tool.type', '品牌型号', reaonly='True')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
- online_time = fields.Char('开机时长')
+ online_time = fields.Char('开机时长', reaonly='True')
- offline_time = fields.Char('关机时长')
- offline_nums = fields.Integer('关机次数')
+ offline_time = fields.Char('关机时长', reaonly='True')
+ offline_nums = fields.Integer('关机次数', reaonly='True')
# 待机时长
- idle_time = fields.Char('待机时长')
+ idle_time = fields.Char('待机时长', reaonly='True')
# 待机率
- idle_rate = fields.Char('待机率')
+ idle_rate = fields.Char('待机率', reaonly='True')
- work_time = fields.Char('加工时长')
- work_rate = fields.Char('可用率')
- fault_time = fields.Char('故障时长')
- fault_rate = fields.Char('故障率')
- fault_nums = fields.Integer('故障次数')
+ 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')
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
@@ -82,11 +85,13 @@ class SfMaintenanceEquipmentOEELogDetail(models.Model):
_name = 'maintenance.equipment.oee.log.detail'
_description = '设备运行日志详情'
- sequence = fields.Integer('序号')
+ # sequence = fields.Integer('序号', related='id')
time = fields.Datetime('时间')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
production_id = fields.Many2one('mrp.production', '加工工单')
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
+ # equipment_code = fields.Char('设备编码', related='log_id.equipment_code')
+ equipment_code = fields.Char('设备编码', readonly='True')
diff --git a/sf_maintenance/views/maintenance_logs_views.xml b/sf_maintenance/views/maintenance_logs_views.xml
index 0d172285..30f1c79c 100644
--- a/sf_maintenance/views/maintenance_logs_views.xml
+++ b/sf_maintenance/views/maintenance_logs_views.xml
@@ -159,6 +159,8 @@
+
+
@@ -202,7 +204,7 @@
-
+
@@ -219,7 +221,7 @@
-
+
@@ -263,7 +265,7 @@
maintenance.equipment.oee.log.detail
-
+
@@ -283,7 +285,7 @@
-
+