diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 47b21dc7..9be466f5 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -206,7 +206,7 @@ class QualityCheck(models.Model):
('NG', 'NG')
], string='出厂检验报告结果', default='OK')
measure_operator = fields.Many2one('res.users', string='操机员')
- quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager', store=True)
+ quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager')
@api.depends('measure_line_ids')
def _compute_quality_manager(self):
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index 38de9e4e..528003e3 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -267,7 +267,7 @@
-
+
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 4385937c..13394ddb 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -5,7 +5,7 @@ import json
import base64
import logging
import psycopg2
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
from odoo import http, fields
from odoo.http import request
@@ -414,7 +414,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单计划量切换为CNC工单
plan_data_total_counts = work_order_obj.search_count(
- [('production_id.production_line_id.name', '=', line),
+ [('production_line_id.name', '=', line), ('id', '!=', 8061),
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
# # 工单完成量
@@ -423,13 +423,13 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单完成量切换为CNC工单
plan_data_finish_counts = work_order_obj.search_count(
- [('production_id.production_line_id.name', '=', line),
+ [('production_line_id.name', '=', line),
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
# 超期完成量
# 搜索所有已经完成的工单
plan_data_overtime = work_order_obj.search([
- ('production_id.production_line_id.name', '=', line),
+ ('production_line_id.name', '=', line),
('state', 'in', ['done']),
('routing_type', '=', 'CNC加工')
])
@@ -448,9 +448,14 @@ class Sf_Dashboard_Connect(http.Controller):
])
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
- faulty_plans = plan_data.filtered(lambda p: any(
- result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
- ))
+ # faulty_plans = plan_data.filtered(lambda p: any(
+ # result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
+ # ))
+
+ faulty_plans = request.env['quality.check'].sudo().search([
+ ('operation_id.name', '=', 'CNC加工'),
+ ('quality_state', 'in', ['fail'])
+ ])
# 查找制造订单取消与归档的数量
cancel_order_count = production_obj.search_count(
@@ -567,7 +572,7 @@ class Sf_Dashboard_Connect(http.Controller):
"""
res = {'status': 1, 'message': '成功', 'data': {}}
# plan_obj = request.env['sf.production.plan'].sudo()
- plan_obj = request.env['mrp.workorder'].sudo().search([('routing_type', '=', 'CNC加工')])
+ # plan_obj = request.env['mrp.workorder'].sudo().search([('routing_type', '=', 'CNC加工')])
line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
@@ -617,11 +622,19 @@ class Sf_Dashboard_Connect(http.Controller):
for time_interval in time_intervals:
start_time, end_time = time_interval
- orders = plan_obj.search([
- ('production_id.production_line_id.name', '=', line),
+ # orders = plan_obj.search([
+ # ('production_line_id.name', '=', line),
+ # ('state', 'in', ['done']),
+ # (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
+ # (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
+ # ])
+
+ orders = request.env['mrp.workorder'].sudo().search([
+ ('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
+ ('production_line_id.name', '=', line),
('state', 'in', ['done']),
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
- (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
+ (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S'))
])
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
@@ -638,18 +651,22 @@ class Sf_Dashboard_Connect(http.Controller):
for date in date_list:
next_day = date + timedelta(days=1)
- orders = plan_obj.search([('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
- (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
- (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
- ])
-
- rework_orders = plan_obj.search(
- [('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
+ orders = request.env['mrp.workorder'].sudo().search(
+ [('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
+ ('routing_type', '=', 'CNC加工'),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
- not_passed_orders = plan_obj.search(
+
+ rework_orders = request.env['mrp.workorder'].sudo().search(
+ [('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
+ ('routing_type', '=', 'CNC加工'),
+ (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
+ (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
+ ])
+ not_passed_orders = request.env['mrp.workorder'].sudo().search(
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']),
+ ('routing_type', '=', 'CNC加工'),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
@@ -751,11 +768,14 @@ class Sf_Dashboard_Connect(http.Controller):
for line in line_list:
# 未完成订单
- not_done_orders = plan_obj.search(
- [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
- ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
+ # not_done_orders = plan_obj.search(
+ # [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
+ # ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
+ # ])
+ not_done_orders = request.env['mrp.workorder'].sudo().search(
+ [('production_line_id.name', '=', line), ('state', 'in', ['ready', 'progress']),
+ ('routing_type', '=', 'CNC加工')
])
- # print(not_done_orders)
# 完成订单
# 获取当前时间,并计算24小时前的时间
@@ -807,16 +827,18 @@ class Sf_Dashboard_Connect(http.Controller):
'draft': '待排程',
'done': '已排程',
'processing': '生产中',
- 'finished': '已完成'
+ 'finished': '已完成',
+ 'ready': '待加工',
+ 'progress': '生产中',
}
line_dict = {
'sequence': id_to_sequence[order.id],
- 'workorder_name': order.name,
+ 'workorder_name': order.production_id.name,
'blank_name': blank_name,
'material': material,
'dimensions': dimensions,
- 'order_qty': order.product_qty,
+ 'order_qty': 1,
'state': state_dict[order.state],
}
@@ -897,15 +919,17 @@ class Sf_Dashboard_Connect(http.Controller):
cur.execute(sql2, (item,))
result2 = cur.fetchall()
- # print('result2========', result2)
- #
+
for row in result:
res['data'][item] = {'idle_count': row[0]}
alarm_count = []
for row in result2:
alarm_count.append(row[1])
if row[0]:
- total_alarm_time += abs(float(row[0]))
+ if float(row[0]) >= 28800:
+ continue
+ # total_alarm_time += abs(float(row[0]))
+ total_alarm_time += float(row[0])
else:
total_alarm_time += 0.0
if len(list(set(alarm_count))) == 1:
@@ -915,6 +939,7 @@ class Sf_Dashboard_Connect(http.Controller):
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
@@ -1332,7 +1357,7 @@ class Sf_Dashboard_Connect(http.Controller):
for result in results:
alarm_last_24_nums.append(result[1])
if result[0]:
- if float(result[0]) >= 1000:
+ if float(result[0]) >= 28800:
continue
alarm_last_24_time += float(result[0])
else:
@@ -1350,7 +1375,7 @@ class Sf_Dashboard_Connect(http.Controller):
for result in results:
alarm_all_nums.append(result[1])
if result[0]:
- if float(result[0]) >= 1000:
+ if float(result[0]) >= 28800:
continue
alarm_all_time += float(result[0])
else:
@@ -1385,3 +1410,207 @@ class Sf_Dashboard_Connect(http.Controller):
conn.close()
return json.dumps(res)
+
+ @http.route('/api/utilization/rate', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
+ def UtilizationRate(self, **kw):
+ """
+ 获取稼动率
+ """
+ logging.info("kw=:%s" % kw)
+ res = {'status': 1, 'message': '成功', 'data': {}}
+ # 获取请求的机床数据
+ machine_list = ast.literal_eval(kw['machine_list'])
+ line = kw['line']
+ orders = request.env['mrp.workorder'].sudo().search([
+ ('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
+ ('production_line_id.name', '=', line),
+ ('state', 'in', ['done'])
+ ])
+
+ faulty_plans = request.env['quality.check'].sudo().search([
+ ('operation_id.name', '=', 'CNC加工'),
+ ('quality_state', 'in', ['fail'])
+ ])
+
+ # 计算时间范围
+ now = datetime.now()
+ today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
+ month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
+
+ total_power_on_time = 0
+ month_power_on_time = 0
+ today_power_on_time = 0
+ today_power_on_dict = {}
+
+ today_data = []
+ month_data = []
+ today_check_ng = []
+ month_check_ng = []
+
+ total_alarm_time = 0
+ today_alarm_time = 0
+ month_alarm_time = 0
+
+ for order in orders:
+ time = datetime.strptime(order.date_finished, "%Y-%m-%d %H:%M:%S")
+ if time >= today_start:
+ today_data.append(order)
+ if time >= month_start:
+ month_data.append(order)
+
+ for faulty_plan in faulty_plans:
+ time = faulty_plan.write_date
+ if time >= today_start:
+ today_check_ng.append(faulty_plan)
+ if time >= month_start:
+ month_check_ng.append(faulty_plan)
+
+ # 连接数据库
+ conn = psycopg2.connect(**db_config)
+ for item in machine_list:
+ with conn.cursor() as cur:
+ cur.execute("""
+ (
+ SELECT power_on_time, 'latest' AS record_type
+ FROM device_data
+ WHERE device_name = %s
+ AND power_on_time IS NOT NULL
+ ORDER BY time DESC
+ LIMIT 1
+ )
+ UNION ALL
+ (
+ SELECT power_on_time, 'month_first' AS record_type
+ FROM device_data
+ WHERE device_name = %s
+ AND power_on_time IS NOT NULL
+ AND time >= date_trunc('month', CURRENT_DATE) -- ✅ 修复日期函数
+ AND time < (date_trunc('month', CURRENT_DATE) + INTERVAL '1 month')::date
+ ORDER BY time ASC
+ LIMIT 1
+ )
+ UNION ALL
+ (
+ SELECT power_on_time, 'day_first' AS record_type
+ FROM device_data
+ WHERE device_name = %s
+ AND power_on_time IS NOT NULL
+ AND time::date = CURRENT_DATE -- ✅ 更高效的写法
+ ORDER BY time ASC
+ LIMIT 1
+ );
+ """, (item, item, item))
+ results = cur.fetchall()
+ print(results)
+ if len(results) >= 1:
+ total_power_on_time += convert_to_seconds(results[0][0])
+ else:
+ total_power_on_time += 0
+ if len(results) >= 2:
+ month_power_on_time += convert_to_seconds(results[1][0])
+ else:
+ month_power_on_time += 0
+ if len(results) >= 3:
+ today_power_on_time += convert_to_seconds(results[2][0])
+ today_power_on_dict[item] = today_power_on_time
+ else:
+ today_power_on_time += 0
+ print(total_power_on_time, month_power_on_time, today_power_on_time)
+
+ 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
+ ORDER BY alarm_start_time, time;
+ """, (item,))
+ results = cur.fetchall()
+ today_data = []
+ month_data = []
+
+ for record in results:
+ if record[0]:
+ if float(record[0]) >= 28800:
+ continue
+ total_alarm_time += float(record[0])
+ else:
+ total_alarm_time += 0.0
+ alarm_start = datetime.strptime(record[1], "%Y-%m-%d %H:%M:%S")
+ if alarm_start >= today_start:
+ today_data.append(record)
+ if alarm_start >= month_start:
+ month_data.append(record)
+ for today in today_data:
+ if today[0]:
+ if float(today[0]) >= 28800:
+ continue
+ today_alarm_time += float(today[0])
+ else:
+ today_alarm_time += 0.0
+ for month in month_data:
+ if month[0]:
+ if float(month[0]) >= 28800:
+ continue
+ month_alarm_time += float(month[0])
+ else:
+ month_alarm_time += 0.0
+
+ conn.close()
+
+ print('报警时间=============', total_alarm_time, month_alarm_time, today_alarm_time)
+ logging.info("报警时间=%s" % total_alarm_time)
+ logging.info("报警时间=%s" % month_alarm_time)
+ logging.info("报警时间=%s" % today_alarm_time)
+ # 计算时间开动率(累计、月、日)
+ if total_power_on_time:
+ total_power_on_rate = (total_power_on_time - total_alarm_time) / total_power_on_time
+ else:
+ total_power_on_rate = 0
+ if month_power_on_time:
+ month_power_on_rate = (total_power_on_time - month_power_on_time - month_alarm_time) / month_power_on_time
+ else:
+ month_power_on_rate = 0
+ if today_power_on_time:
+ today_power_on_rate = (total_power_on_time - today_power_on_time - today_alarm_time) / today_power_on_time
+ else:
+ today_power_on_rate = 0
+ print("总开动率: %s" % total_power_on_rate)
+ print("月开动率: %s" % month_power_on_rate)
+ print("日开动率: %s" % today_power_on_rate)
+
+ # 计算性能开动率(累计、月、日)
+ print('===========',orders)
+ print(len(orders))
+ total_performance_rate = len(orders) * 30 * 60 / (total_power_on_time - total_alarm_time)
+ month_performance_rate = len(month_data) * 30 * 60 / (month_power_on_time - month_alarm_time)
+ today_performance_rate = len(today_data) * 30 * 60 / (today_power_on_time - today_alarm_time) if today_power_on_time != 0 else 0
+ print("总性能率: %s" % total_performance_rate)
+ print("月性能率: %s" % month_performance_rate)
+ print("日性能率: %s" % today_performance_rate)
+
+ # 计算累计合格率
+ total_pass_rate = (len(orders) - len(today_check_ng)) / len(orders) if len(orders) != 0 else 0
+ month_pass_rate = (len(month_data) - len(month_check_ng)) / len(month_data) if len(month_data) != 0 else 0
+ today_pass_rate = (len(today_data) - len(today_check_ng)) / len(today_data) if len(today_data) != 0 else 0
+ print("总合格率: %s" % total_pass_rate)
+ print("月合格率: %s" % month_pass_rate)
+ print("日合格率: %s" % today_pass_rate)
+
+ # # 返回数据
+ # res['data'][item] = {
+ # 'total_utilization_rate': total_power_on_rate * total_performance_rate * total_pass_rate,
+ # 'month_utilization_rate': month_power_on_rate * month_performance_rate * month_pass_rate,
+ # 'today_utilization_rate': today_power_on_rate * today_performance_rate * today_pass_rate,
+ # }
+ res['data'] = {
+ 'total_utilization_rate': total_power_on_rate * total_performance_rate * total_pass_rate,
+ 'month_utilization_rate': month_power_on_rate * month_performance_rate * month_pass_rate,
+ 'today_utilization_rate': today_power_on_rate * today_performance_rate * today_pass_rate,
+ }
+
+ return json.dumps(res)
+
+
+
+
+
diff --git a/sf_manufacturing/models/agv_scheduling.py b/sf_manufacturing/models/agv_scheduling.py
index cc621727..33522182 100644
--- a/sf_manufacturing/models/agv_scheduling.py
+++ b/sf_manufacturing/models/agv_scheduling.py
@@ -87,11 +87,12 @@ class AgvScheduling(models.Model):
agv_route_type: AGV任务类型
workorders: 工单
"""
+ scheduling = None
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s】' % (agv_start_site_name, agv_route_type, workorders))
if not workorders:
raise UserError(_('工单不能为空'))
- agv_start_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)], limit=1)
- if not agv_start_site:
+ agv_start_sites = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)])
+ if not agv_start_sites:
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
# 如果存在相同任务类型工单的AGV调度任务,则提示错误
agv_scheduling = self.sudo().search([
@@ -107,24 +108,32 @@ class AgvScheduling(models.Model):
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
)
+ # 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
+ agv_routes = self.env['sf.agv.task.route'].sudo().search([
+ ('route_type', '=', agv_route_type),
+ ('start_site_id', 'in', agv_start_sites.ids)
+ ])
vals = {
- 'start_site_id': agv_start_site.id,
'agv_route_type': agv_route_type,
'workorder_ids': workorders.ids,
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
'task_create_time': fields.Datetime.now()
}
- # 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
- agv_routes = self.env['sf.agv.task.route'].sudo().search([
- ('route_type', '=', agv_route_type),
- ('start_site_id', '=', agv_start_site.id)
- ])
if not agv_routes:
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
+ # 如果路线中包含起点与终点相同的接驳站,则不创建AGV调度任务
+ if agv_routes.filtered(lambda r: r.start_site_id.name == r.end_site_id.name):
+ return True
+ # 配送类型相同的接驳站为同一个,取第一个即可
+ vals.update({
+ 'start_site_id': agv_routes[0].start_site_id.id,
+ })
idle_route = None
if len(agv_routes) == 1:
idle_route = agv_routes[0]
- vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
+ vals.update({
+ 'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id
+ })
else:
# 判断终点接驳站是否为空闲
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
@@ -132,7 +141,10 @@ class AgvScheduling(models.Model):
# 将空闲的路线按照终点接驳站名称排序
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
idle_route = idle_routes[0]
- vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
+ vals.update({
+ 'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id
+ })
+
try:
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
# 触发空闲接驳站状态更新,触发新任务下发
@@ -142,7 +154,7 @@ class AgvScheduling(models.Model):
except Exception as e:
_logger.error('添加AGV调度任务失败: %s', e)
raise UserError(_('添加AGV调度任务失败: %s', e))
-
+
return scheduling
def on_site_state_change(self, agv_site_id, agv_site_state):
diff --git a/sf_manufacturing/models/agv_setting.py b/sf_manufacturing/models/agv_setting.py
index 820ed539..d33ea9b4 100644
--- a/sf_manufacturing/models/agv_setting.py
+++ b/sf_manufacturing/models/agv_setting.py
@@ -24,7 +24,7 @@ class AgvSetting(models.Model):
# name必须唯一
_sql_constraints = [
- ('name_uniq', 'unique (name)', '站点编号必须唯一!'),
+ ('name_uniq', 'unique (name, workcenter_id)', '同一工作中心的站点编号必须唯一!'),
]
# def update_site_state(self):
@@ -68,11 +68,12 @@ class AgvSetting(models.Model):
"""
if isinstance(agv_site_state_arr, dict):
for agv_site_name, is_occupy in agv_site_state_arr.items():
- agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
- if agv_site:
- agv_site.state = is_occupy
+ agv_sites = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
+ if agv_sites:
+ agv_sites.state = is_occupy
if notify:
- self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
+ for agv_site in agv_sites:
+ self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
else:
_logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
raise UserError("更新失败:接驳站站点错误!")
diff --git a/sf_manufacturing/views/agv_setting_views.xml b/sf_manufacturing/views/agv_setting_views.xml
index 62e3c623..b289f4b6 100644
--- a/sf_manufacturing/views/agv_setting_views.xml
+++ b/sf_manufacturing/views/agv_setting_views.xml
@@ -15,6 +15,23 @@
+
+ agv.site.form
+ sf.agv.site
+
+
+
+
+
AGV站点
sf.agv.site
@@ -39,7 +56,8 @@
-
+
diff --git a/sf_manufacturing/wizard/workpiece_delivery_wizard.py b/sf_manufacturing/wizard/workpiece_delivery_wizard.py
index c41a7619..b100325e 100644
--- a/sf_manufacturing/wizard/workpiece_delivery_wizard.py
+++ b/sf_manufacturing/wizard/workpiece_delivery_wizard.py
@@ -117,17 +117,30 @@ class WorkpieceDeliveryWizard(models.TransientModel):
item.button_finish()
# return scheduling.read()[0]
- return {
- 'type': 'ir.actions.client',
- 'tag': 'display_notification',
- 'target': 'new',
- 'params': {
- 'message': f'任务下发成功!AGV任务调度编号为【{scheduling.name}】',
- 'type': 'success',
- 'sticky': False,
- 'next': {'type': 'ir.actions.act_window_close'},
+ if isinstance(scheduling, bool) and scheduling is True:
+ return{
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'target': 'new',
+ 'params': {
+ 'message': f'解除装夹成功',
+ 'type': 'success',
+ 'sticky': False,
+ 'next': {'type': 'ir.actions.act_window_close'},
+ }
+ }
+ else:
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'target': 'new',
+ 'params': {
+ 'message': f'任务下发成功!AGV任务调度编号为【{scheduling.name}】',
+ 'type': 'success',
+ 'sticky': False,
+ 'next': {'type': 'ir.actions.act_window_close'},
+ }
}
- }
except Exception as e:
logging.info('%s任务下发失败:%s', self.delivery_type, e)
raise UserError(f'{self.delivery_type}任务下发失败:{e}') from e
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
index afe7148b..14a42065 100644
--- a/sf_quality/data/insepection_report_template.xml
+++ b/sf_quality/data/insepection_report_template.xml
@@ -61,6 +61,29 @@
+
+
+
@@ -73,7 +96,7 @@
| 产品名称: |
- |
+ |
材料: |
|
@@ -187,4 +210,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ | 产品名称: |
+ |
+ 材料: |
+ |
+
+
+ | 图号: |
+ |
+ 日期: |
+ |
+
+
+ | 总数量: |
+ |
+ 检验数量: |
+ |
+
+
+
+
检验结果
+
+
+
+
+ 检测项目 (图示尺寸) |
+ 测量值 |
+ 判定 |
+ 备注 |
+
+
+
+ | 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+

+
+
+
+
+
检验结论:
+
+
+ 合格
+
+
+
+ 合格
+
+
+
+ 不合格
+
+
+
+ 不合格
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_quality/data/report_actions.xml b/sf_quality/data/report_actions.xml
index 7ee44f63..59338103 100644
--- a/sf_quality/data/report_actions.xml
+++ b/sf_quality/data/report_actions.xml
@@ -18,7 +18,7 @@
预览检验报告
quality.check
qweb-html
- sf_quality.report_quality_inspection
+ sf_quality.html_report_quality_inspection
report
\ No newline at end of file
diff --git a/sf_tool_management/__manifest__.py b/sf_tool_management/__manifest__.py
index 8baa540f..837a8f2e 100644
--- a/sf_tool_management/__manifest__.py
+++ b/sf_tool_management/__manifest__.py
@@ -38,6 +38,7 @@
'web.assets_qweb': [
],
'web.assets_backend': [
+ 'sf_tool_management/static/src/change.scss'
]
},
diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py
index 40100a93..3cbaf974 100644
--- a/sf_tool_management/models/base.py
+++ b/sf_tool_management/models/base.py
@@ -387,7 +387,7 @@ class FunctionalToolAssembly(models.Model):
else:
raise ValidationError('刀柄选择错误,请重新确认!!!')
else:
- location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode)])
+ location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode.upper())])
if location:
if location == record.integral_freight_barcode_id:
tool_assembly_id.integral_verify = True
@@ -782,10 +782,11 @@ class FunctionalToolAssembly(models.Model):
"""根据BOM对刀具物料进行初始配置"""
options = bom.get('options')
# 配置刀柄信息
- for handle_id in bom.get('handle_ids'):
+ handle_ids = self._get_old_tool_material_lot(bom.get('handle_ids'))
+ for handle_id in handle_ids:
if handle_id:
if not self.handle_product_id:
- self.handle_product_id = handle_id.id
+ self.handle_product_id = handle_id.product_id.id
break
# 刀柄之外的物料配置
@@ -820,19 +821,20 @@ class FunctionalToolAssembly(models.Model):
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
stock_quant = self.env['stock.quant'].sudo().search(
[('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')],
- order='lot_id', limit=1)
+ order='lot_id')
if stock_quant:
- return stock_quant.lot_id
+ return [quant.lot_id for quant in stock_quant]
else:
raise ValidationError(f'【{material_ids[0].cutting_tool_material_id.name}】物料库存不足,请先进行盘点或采购')
- def _get_shelf_location_lot(self, lot_id):
+ def _get_shelf_location_lot(self, lot_ids):
"""根据所给的刀具物料批次号,返回一个刀具物料货位、批次信息"""
- location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id)])
- if not location_lots:
- raise ValidationError(f'没有查询到批次为【{lot_id.name}】物料的货位信息!')
- else:
- return location_lots[0]
+ for lot_id in lot_ids:
+ location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id)])
+ if location_lots:
+ return location_lots[0]
+ raise ValidationError(f'【{lot_ids[0].product_id.cutting_tool_material_id.name}】物料在货位库存不足,请先进行盘点入库')
+
def _get_inventory_bom(self, inventory_id):
"""获取BOM的刀具物料产品信息"""
@@ -1240,7 +1242,7 @@ class FunctionalToolDismantle(models.Model):
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
domain=[('functional_tool_status', '!=', '已拆除'),
- ('current_location', '=', '刀具房')])
+ ('current_location', 'in', ['刀具房', '线边刀库'])])
@api.onchange('functional_tool_id')
def _onchange_functional_tool_id(self):
@@ -1437,14 +1439,26 @@ class FunctionalToolDismantle(models.Model):
def confirmation_disassembly(self):
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
code = self.code
+ context = self.env.context
if self.functional_tool_id.functional_tool_status == '已拆除':
raise ValidationError('Rfid为【%s】名称为【%s】的功能刀具已经拆解,请勿重复操作!' % (
self.functional_tool_id.rfid_dismantle, self.name))
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
elif self.functional_tool_id.functional_tool_status != '报警':
- if self.functional_tool_id.tool_room_num == 0:
+ if self.functional_tool_id.current_location == '机内刀库':
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
self.rfid, self.functional_tool_id.current_location))
+ elif not context.get('TRUE_DISASSEMBLE') and self.functional_tool_id.current_location == '线边刀库':
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'sf.functional.tool.dismantle.wiard',
+ 'name': '刀具寿命未到期',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_functional_tool_dismantle_id': self.id,
+ 'TRUE_DISASSEMBLE': True}
+ }
# 目标重复校验
self.location_duplicate_check()
datas = {'scrap': [], 'picking': []}
@@ -1521,11 +1535,18 @@ class FunctionalToolDismantle(models.Model):
'functional_tool_name': self.functional_tool_id.name,
'handle_code_id': self.handle_lot_id.id,
'handle_product_id': self.handle_product_id.id,
+ 'functional_tool_diameter': self.functional_tool_id.functional_tool_diameter,
+ 'knife_tip_r_angle': self.functional_tool_id.knife_tip_r_angle,
+ 'tool_loading_length': self.functional_tool_id.tool_loading_length,
+ 'functional_tool_length': self.functional_tool_id.functional_tool_length,
'loading_task_source': '3',
'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
'reason_for_applying': '刀具寿命到期'
})
+ # 将新的组装单更新到对应的功能刀具安全库存的组装单列表中
+ self.functional_tool_id.safe_inventory_id.sudo().sf_functional_tool_assembly_ids = [(4, assembly_id.id)]
+
return {
'type': 'ir.actions.act_window',
'res_model': 'sf.functional.tool.assembly',
diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py
index 63a31b82..498d9436 100644
--- a/sf_tool_management/models/functional_tool.py
+++ b/sf_tool_management/models/functional_tool.py
@@ -10,6 +10,7 @@ from odoo.exceptions import ValidationError
class FunctionalCuttingToolEntity(models.Model):
_name = 'sf.functional.cutting.tool.entity'
+ _inherit = ['mail.thread']
_description = '功能刀具列表'
_order = 'functional_tool_status'
@@ -41,7 +42,7 @@ class FunctionalCuttingToolEntity(models.Model):
max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True)
alarm_value = fields.Integer(string='报警值(min)', readonly=True)
used_value = fields.Integer(string='已使用值(min)', readonly=True)
- functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')],
+ functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], tracking=True,
string='状态', store=True, default='正常')
current_location_id = fields.Many2one('stock.location', string='当前位置', compute='_compute_current_location_id',
store=True)
@@ -62,16 +63,27 @@ class FunctionalCuttingToolEntity(models.Model):
for item in self:
if item:
if item.functional_tool_status == '报警':
- # 创建报警刀具拆解单
- self.env['sf.functional.tool.dismantle'].sudo().create({
- 'functional_tool_id': item.ids[0],
- 'dismantle_cause': '寿命到期报废'
- })
- # 创建刀具报警记录
- self.env['sf.functional.tool.warning'].sudo().create({
- 'rfid': item.rfid,
- 'functional_tool_id': item.ids[0]
- })
+ self.create_tool_dismantle()
+
+ def set_functional_tool_status(self):
+ # self.write({
+ # 'functional_tool_status': '报警'
+ # })
+ self.functional_tool_status = '报警'
+ self.create_tool_dismantle()
+
+ def create_tool_dismantle(self):
+ for item in self:
+ # 创建报警刀具拆解单
+ self.env['sf.functional.tool.dismantle'].sudo().create({
+ 'functional_tool_id': item.ids[0],
+ 'dismantle_cause': '寿命到期报废'
+ })
+ # 创建刀具报警记录
+ self.env['sf.functional.tool.warning'].sudo().create({
+ 'rfid': item.rfid,
+ 'functional_tool_id': item.ids[0]
+ })
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
'current_shelf_location_id', 'stock_num')
@@ -263,7 +275,7 @@ class FunctionalCuttingToolEntity(models.Model):
functional_tool_model_ids.append(functional_tool_model.id)
return [(6, 0, functional_tool_model_ids)]
- dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', store=True)
+ dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', tracking=True, store=True)
dismantle_ids = fields.One2many('sf.functional.tool.dismantle', 'functional_tool_id', '拆解单')
@api.depends('dismantle_ids')
diff --git a/sf_tool_management/models/maintenance_equipment.py b/sf_tool_management/models/maintenance_equipment.py
index 12c7f8f3..a3375075 100644
--- a/sf_tool_management/models/maintenance_equipment.py
+++ b/sf_tool_management/models/maintenance_equipment.py
@@ -107,11 +107,17 @@ class SfMaintenanceEquipment(models.Model):
if functional_tool_id.current_location != '机内刀库':
# 对功能刀具进行移动到生产线
functional_tool_id.tool_inventory_displacement_out()
- functional_tool_id.write({
- 'max_lifetime_value': data['MaxLife'],
- 'used_value': data['UseLife'],
- 'functional_tool_status': tool_install_time.get(data['State'])
- })
+ data_tool = {
+ 'max_lifetime_value': data['MaxLife'],
+ 'used_value': data['UseLife'],
+ 'functional_tool_status': tool_install_time.get(data['State'])
+ }
+ if (functional_tool_id.functional_tool_status != '报警'
+ and tool_install_time.get(data['State']) == '报警'):
+ functional_tool_id.write(data_tool)
+ functional_tool_id.create_tool_dismantle()
+ else:
+ functional_tool_id.write(data_tool)
else:
logging.info('获取的【%s】设备不存在!!!' % data['DeviceId'])
else:
diff --git a/sf_tool_management/security/ir.model.access.csv b/sf_tool_management/security/ir.model.access.csv
index 8c188464..1ad8a439 100644
--- a/sf_tool_management/security/ir.model.access.csv
+++ b/sf_tool_management/security/ir.model.access.csv
@@ -40,4 +40,7 @@ access_sf_functional_tool_dismantle_group_sf_tool_user,sf.functional.tool.disman
access_sf_functional_tool_dismantle_group_plan_dispatch,sf.functional.tool.dismantle_group_plan_dispatch,model_sf_functional_tool_dismantle,sf_base.group_plan_dispatch,1,0,0,0
access_jikimo_bom,jikimo.bom,model_jikimo_bom,base.group_user,1,1,1,1
-access_jikimo_bom_wizard,jikimo.bom.wizard,model_jikimo_bom_wizard,base.group_user,1,1,1,1
\ No newline at end of file
+access_jikimo_bom_wizard,jikimo.bom.wizard,model_jikimo_bom_wizard,base.group_user,1,1,1,1
+
+access_sf_functional_tool_dismantle_wiard,sf.functional.tool.dismantle.wiard,model_sf_functional_tool_dismantle_wiard,sf_base.group_sf_tool_user,1,1,1,0
+access_sf_functional_tool_dismantle_wiard_group_plan_dispatch,sf.functional.tool.dismantle.wiard,model_sf_functional_tool_dismantle_wiard,sf_base.group_plan_dispatch,1,0,0,0
diff --git a/sf_tool_management/static/images/replaceIcon.png b/sf_tool_management/static/images/replaceIcon.png
new file mode 100644
index 00000000..12d2852f
Binary files /dev/null and b/sf_tool_management/static/images/replaceIcon.png differ
diff --git a/sf_tool_management/static/src/change.scss b/sf_tool_management/static/src/change.scss
index 4c857c8c..9cf26b0a 100644
--- a/sf_tool_management/static/src/change.scss
+++ b/sf_tool_management/static/src/change.scss
@@ -1,17 +1,47 @@
-.modal-content .o_cp_buttons {
- display:none
-}
+// .modal-content .o_cp_buttons {
+// display:none
+// }
-.modal-content .o_control_panel {
- display:none
-}
+// .modal-content .o_control_panel {
+// display:none
+// }
-.modal-content .o_list_button {
+// .modal-content .o_list_button {
+
+// }
+
+// .o_form_view .o_field_widget .o_list_renderer {
+// width: 100%!important;
+// margin:0 auto;
+// overflow: auto;
+// }
+.o_field_widget.o_readonly_modifier.o_field_char.text-success[name=handle_freight_rfid],
+.o_field_widget.o_readonly_modifier.o_field_many2one.text-success[name=integral_freight_barcode_id] {
+ a.text-success{
+ span {
+ color: #999;
+ }
+ }
}
-
-.o_form_view .o_field_widget .o_list_renderer {
- width: 100%!important;
- margin:0 auto;
- overflow: auto;
+.custom_group:has(.text-success){
+ position: relative;
+ &::after{
+ content: '';
+ display: block;
+ width: 72px;
+ height: 72px;
+ background: url('/sf_tool_management/static/images/replaceIcon.png') no-repeat center center;
+ background-size: 100%;
+ position: absolute;
+ bottom: 20px;
+ left: 300px;
+ }
}
+.o_field_widget.o_readonly_modifier.o_field_char.text-success[name=handle_freight_rfid] {
+ display: flex;
+ align-items: center;
+ > span {
+ color: #999;
+ }
+}
\ No newline at end of file
diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml
index 2b872053..25b9346a 100644
--- a/sf_tool_management/views/functional_tool_views.xml
+++ b/sf_tool_management/views/functional_tool_views.xml
@@ -42,6 +42,7 @@
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index 8f986858..fd05b4ef 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -531,10 +531,10 @@
-
+
-
+
@@ -554,15 +554,15 @@
-
-
+
+
-
+
@@ -582,8 +582,8 @@
-
-
+
+
@@ -607,8 +607,8 @@
-
-
+
+
@@ -631,8 +631,8 @@
-
-
+
+
diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py
index d1797855..633901d8 100644
--- a/sf_tool_management/wizard/wizard.py
+++ b/sf_tool_management/wizard/wizard.py
@@ -770,3 +770,12 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# }
+class FunctionalToolDismantle(models.TransientModel):
+ _name = 'sf.functional.tool.dismantle.wiard'
+ _description = '功能刀具拆解二次确认'
+
+ functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', '拆解单')
+
+ def confirm(self):
+ self.functional_tool_dismantle_id.confirmation_disassembly()
+ return True
diff --git a/sf_tool_management/wizard/wizard_view.xml b/sf_tool_management/wizard/wizard_view.xml
index 53a45d85..73770342 100644
--- a/sf_tool_management/wizard/wizard_view.xml
+++ b/sf_tool_management/wizard/wizard_view.xml
@@ -444,4 +444,19 @@
new
+
+
+
+ 刀具拆解
+ sf.functional.tool.dismantle.wiard
+
+
+
+
\ No newline at end of file
diff --git a/sf_warehouse/security/ir.model.access.csv b/sf_warehouse/security/ir.model.access.csv
index 88514fd3..1510e854 100644
--- a/sf_warehouse/security/ir.model.access.csv
+++ b/sf_warehouse/security/ir.model.access.csv
@@ -150,4 +150,4 @@ access_sf_shelf_lot_group_user,sf.shelf.location.lot.group_user,model_sf_shelf_l
access_ir_model_group_sf_stock_user,ir_model_group_sf_stock_user,base.model_ir_model,sf_base.group_sf_stock_user,1,1,0,0
-access_mrp_workorder_group_sf_stock_user,mrp_workorder_group_sf_stock_user,mrp.model_mrp_workorder,sf_base.group_sf_stock_user,1,0,0,0
+access_mrp_workorder_group_sf_stock_user,mrp_workorder_group_sf_stock_user,mrp.model_mrp_workorder,sf_base.group_sf_stock_user,1,1,0,0
diff --git a/tool_service/装夹自动保存检测文件服务/app.py b/tool_service/装夹自动保存检测文件服务/app.py
index ba41f666..06a0f2bd 100644
--- a/tool_service/装夹自动保存检测文件服务/app.py
+++ b/tool_service/装夹自动保存检测文件服务/app.py
@@ -6,10 +6,7 @@ import win32gui
import win32con
import logging
import time
-
-# 配置日志记录
-logging.basicConfig(filename='service.log', level=logging.INFO,
- format='%(asctime)s - %(levelname)s - %(message)s')
+import re
app = FastAPI()
@@ -19,11 +16,11 @@ class FileUploadRequest(BaseModel):
# FTP 服务器配置信息
-ftp_host = '110.52.114.162'
-ftp_port = 10021
+ftp_host = '47.119.33.43'
+ftp_port = 21
ftp_user = 'ftpuser'
-ftp_password = '123456'
-ftp_directory = '/home/ftp/ftp_root/ThreeTest/XT/Before/'
+ftp_password = 'FTPftp123'
+ftp_directory = 'ThreeTest/XT/Before/'
def find_child_window(parent_hwnd, class_name):
@@ -63,25 +60,95 @@ def find_child_window_by_title(parent_hwnd, title):
return child_hwnds
+# 获取 ComboBox 的句柄
+def get_combobox_handle(parent_handle):
+ combo_handle = win32gui.FindWindowEx(parent_handle, 0, "ComboBox", None)
+ if combo_handle == 0:
+ raise Exception("ComboBox not found")
+ return combo_handle
+
+
+# 获取 ComboBox 中的所有选项
+def get_combobox_items(combo_handle):
+ count = win32gui.SendMessage(combo_handle, win32con.CB_GETCOUNT, 0, 0)
+ items = []
+ for i in range(count):
+ length = win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXTLEN, i, 0)
+ buffer = win32gui.PyMakeBuffer(length + 1)
+ win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXT, i, buffer)
+ byte_data = buffer.tobytes()
+
+ # 尝试多种编码方式
+ text = None
+ # 尝试多种编码方式,包括utf-16le
+ for encoding in ['utf-16le', 'utf-8', 'gbk', 'latin-1']:
+ try:
+ decoded_text = byte_data.decode(encoding).rstrip('\x00')
+ # 如果解码后的文本看起来是合理的,就接受它
+ if any(char.isprintable() for char in decoded_text):
+ text = decoded_text
+ break
+ except UnicodeDecodeError:
+ continue
+
+ # 如果所有解码方式都失败,或者内容仍有大量乱码,显示为十六进制字符串
+ if text is None or not all(char.isprintable() or char.isspace() for char in text):
+ text = byte_data.hex()
+
+ items.append(text)
+ return items
+
+
+# 获取当前选定项
+def get_combobox_selected(combo_handle):
+ index = win32gui.SendMessage(combo_handle, win32con.CB_GETCURSEL, 0, 0)
+ if index == -1:
+ return None
+ length = win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXTLEN, index, 0)
+ buffer = win32gui.PyMakeBuffer(1024) # 调整缓冲区大小
+ win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXT, index, buffer)
+
+ # 尝试多种编码方式进行解码
+ for encoding in ['utf-16le', 'utf-8', 'latin-1']:
+ try:
+ # 解码
+ raw_text = buffer.tobytes().decode(encoding, errors='ignore').rstrip('\x00')
+ # 使用正则表达式查找 "数字 + 月" 格式的部分
+ match = re.search(r'\d+月', raw_text)
+ if match:
+ return match.group()
+ # 使用正则表达式提取有效字符(中文、数字、字母和常见标点)
+ filtered_text = re.findall(r'[\w\u4e00-\u9fa5]+', raw_text)
+ # 返回匹配到的第一个有效部分
+ if filtered_text:
+ return filtered_text[0].strip()
+ except UnicodeDecodeError:
+ continue # 尝试下一个编码方式
+
+ # 如果所有解码方式都失败,返回 None
+ return None
+
+
+# 设置 ComboBox 的值
+def set_combobox_value(combo_handle, value):
+ items = get_combobox_items(combo_handle)
+ try:
+ index = items.index(value)
+ win32gui.SendMessage(combo_handle, win32con.CB_SETCURSEL, index, 0)
+ except ValueError:
+ raise Exception("Value not found in ComboBox")
+
+
def set_path_and_save(filename):
- parent_hwnd = win32gui.FindWindow(None, '另存为')
+ parent_hwnd = win32gui.FindWindow(None, '保存Excel文件')
if parent_hwnd == 0:
raise HTTPException(status_code=404, detail="没有找到保存报告的窗口,请检查!")
- # 这里假设“地址:”是你需要的部分标题
- address_hwnds = find_child_window_by_partial_title(parent_hwnd, "地址:")
-
- # 确保找到的窗口句柄有效
- if not address_hwnds:
- raise HTTPException(status_code=404, detail="未找到地址框,请联系管理员!")
-
- # 假设找到的第一个窗口是目标组件
- address_hwnd = address_hwnds[0]
- logging.info(f"找到地址框地址: {win32gui.GetWindowText(address_hwnd)}")
-
- # 设置路径
- local_file_path = os.path.join(win32gui.GetWindowText(address_hwnd).split(' ')[1], filename)
+ combo_handle = get_combobox_handle(parent_hwnd)
+ #logging.info(f"ComboBox Items: {get_combobox_items(combo_handle)}")
+ logging.info(f"Current Selected: {get_combobox_selected(combo_handle)}")
+ local_file_path = "C:\\RationalDMIS64\\Output\\" + get_combobox_selected(combo_handle) + "\\" + filename
logging.info(f"设置路径: {local_file_path}")
path_hwnds = find_child_window(parent_hwnd, 'Edit')
@@ -115,7 +182,6 @@ def wait_for_file_to_save(filepath, timeout=30):
def upload_file_to_ftp(local_file):
-
if not os.path.isfile(local_file):
raise HTTPException(status_code=204, detail="文件未找到")
@@ -157,4 +223,4 @@ async def upload_file(request: FileUploadRequest):
if __name__ == "__main__":
import uvicorn
- uvicorn.run(app, host="0.0.0.0", port=8000)
+ uvicorn.run(app, host="0.0.0.0", port=8999)