Merge remote-tracking branch 'origin/develop' into feature/临时分支
# Conflicts: # sf_message/models/sf_message_stock_picking.py
This commit is contained in:
@@ -3,8 +3,8 @@
|
|||||||
'name': "jikimo_account_process",
|
'name': "jikimo_account_process",
|
||||||
|
|
||||||
'summary': """
|
'summary': """
|
||||||
Short (1 phrase/line) summary of the module's purpose, used as
|
处理会计凭证生成重复名称报错问题
|
||||||
subtitle on modules listing or apps.openerp.com""",
|
""",
|
||||||
|
|
||||||
'description': """
|
'description': """
|
||||||
Long description of module's purpose
|
Long description of module's purpose
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
||||||
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
||||||
<xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath>
|
<!-- <xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 隐藏odoo版本信息 -->
|
<!-- 隐藏odoo版本信息 -->
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_base', 'web_widget_model_viewer', 'mrp_subcontracting', 'purchase_stock', 'uom', ],
|
'depends': ['sf_base', 'mrp_subcontracting', 'purchase_stock', 'uom'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/product_data.xml',
|
'data/product_data.xml',
|
||||||
'data/uom_data.xml',
|
'data/uom_data.xml',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'author': 'jikimo',
|
'author': 'jikimo',
|
||||||
'website': 'https://sf.cs.jikimo.com',
|
'website': 'https://sf.cs.jikimo.com',
|
||||||
'depends': ['web', 'mail', 'sf_base', 'sf_manufacturing', 'barcodes', ],
|
'depends': ['web', 'sf_manufacturing', 'barcodes'],
|
||||||
'data': [
|
'data': [
|
||||||
# 定义权限组放在最上面
|
# 定义权限组放在最上面
|
||||||
# 权限组
|
# 权限组
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ db_config = {
|
|||||||
"user": "postgres",
|
"user": "postgres",
|
||||||
"password": "postgres",
|
"password": "postgres",
|
||||||
"port": "5432",
|
"port": "5432",
|
||||||
"host": "172.16.10.113"
|
"host": "172.16.10.131"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,6 +24,8 @@ def convert_to_seconds(time_str):
|
|||||||
|
|
||||||
if time_str is None:
|
if time_str is None:
|
||||||
return 0
|
return 0
|
||||||
|
if time_str == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
@@ -1306,9 +1308,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
res['data'][item] = {
|
res['data'][item] = {
|
||||||
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
'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_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,
|
'cut_24_time': last_24_time['process_time'] if last_24_time 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_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 else 0,
|
||||||
'alarm_last_24_time': alarm_last_24_time,
|
'alarm_last_24_time': alarm_last_24_time,
|
||||||
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
||||||
'idle_count': idle_count,
|
'idle_count': idle_count,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ def convert_to_seconds(time_str):
|
|||||||
|
|
||||||
if time_str is None:
|
if time_str is None:
|
||||||
return 0
|
return 0
|
||||||
|
if time_str == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
@@ -122,24 +124,37 @@ class SfMaintenanceEquipmentOEE(models.Model):
|
|||||||
real_dict = result_time['data'][self.equipment_code]
|
real_dict = result_time['data'][self.equipment_code]
|
||||||
print('=', result_time)
|
print('=', result_time)
|
||||||
if result_time['status'] == 1:
|
if result_time['status'] == 1:
|
||||||
self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
|
if real_dict['power_on_24_time'] == 0:
|
||||||
real_dict['power_on_24_time'])) / 3600, 2)
|
self.online_time = 0
|
||||||
self.work_time = round(
|
self.idle_time = 0
|
||||||
(convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600,
|
self.idle_rate = 0
|
||||||
2)
|
self.work_rate = 0
|
||||||
|
self.fault_time = 0
|
||||||
|
self.fault_rate = 0
|
||||||
|
self.fault_nums = 0
|
||||||
|
self.idle_nums = 0
|
||||||
|
self.work_time = 0
|
||||||
|
else:
|
||||||
|
self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
|
||||||
|
real_dict['power_on_24_time'])) / 3600, 2)
|
||||||
|
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']
|
||||||
|
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.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:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|||||||
@@ -1197,8 +1197,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if record.is_rework is False:
|
if record.is_rework is False:
|
||||||
if not record.material_center_point:
|
if not record.material_center_point:
|
||||||
raise UserError("坯料中心点为空,请检查")
|
raise UserError("坯料中心点为空,请检查")
|
||||||
if record.X_deviation_angle <= 0:
|
# if record.X_deviation_angle <= 0:
|
||||||
raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
# raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
||||||
record.process_state = '待加工'
|
record.process_state = '待加工'
|
||||||
# record.write({'process_state': '待加工'})
|
# record.write({'process_state': '待加工'})
|
||||||
record.production_id.process_state = '待加工'
|
record.production_id.process_state = '待加工'
|
||||||
@@ -1826,6 +1826,11 @@ class WorkPieceDelivery(models.Model):
|
|||||||
return is_free
|
return is_free
|
||||||
else:
|
else:
|
||||||
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
|
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
|
||||||
|
|
||||||
|
def delivery_avg(self):
|
||||||
|
is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch')
|
||||||
|
if is_agv_task_dispatch:
|
||||||
|
self._delivery_avg()
|
||||||
|
|
||||||
# 配送至avg小车
|
# 配送至avg小车
|
||||||
def _delivery_avg(self):
|
def _delivery_avg(self):
|
||||||
@@ -1886,7 +1891,7 @@ class WorkPieceDelivery(models.Model):
|
|||||||
logging.info('delivery_item-name:%s' % delivery_item.name)
|
logging.info('delivery_item-name:%s' % delivery_item.name)
|
||||||
delivery_item.write({
|
delivery_item.write({
|
||||||
'task_delivery_time': fields.Datetime.now(),
|
'task_delivery_time': fields.Datetime.now(),
|
||||||
'status': '待配送'
|
'status': '已下发'
|
||||||
})
|
})
|
||||||
if delivery_item.type == "上产线":
|
if delivery_item.type == "上产线":
|
||||||
delivery_item.workorder_id.write({'is_delivery': True})
|
delivery_item.workorder_id.write({'is_delivery': True})
|
||||||
|
|||||||
@@ -797,7 +797,7 @@
|
|||||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||||
<!-- <field name="type" readonly="1"/>-->
|
<!-- <field name="type" readonly="1"/>-->
|
||||||
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
||||||
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
|
<button name="delivery_avg" type="object" string="配送" class="oe_highlight"/>
|
||||||
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
|
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
|
||||||
string="历史"/>
|
string="历史"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -66,8 +66,8 @@
|
|||||||
|
|
||||||
|
|
||||||
<record id="template_transfer_inventory_remind" model="jikimo.message.template">
|
<record id="template_transfer_inventory_remind" model="jikimo.message.template">
|
||||||
<field name="menu_id" ref="stock.menu_stock_root"/>
|
<!-- <field name="menu_id" ref="stock.menu_stock_root"/>-->
|
||||||
<field name="action_id" ref="stock.action_picking_tree_ready"/>
|
<!-- <field name="action_id" ref="stock.action_picking_tree_ready"/>-->
|
||||||
<field name="name">调拨入库</field>
|
<field name="name">调拨入库</field>
|
||||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
@@ -75,13 +75,13 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 调拨入库通知:
|
<field name="content">### 调拨入库通知:
|
||||||
单号:调拨入库单[{{name}}]({{request_url}})
|
单号:调拨入库单[{{name}}]({{transfer_inventory_special_url}})
|
||||||
事项:完成刀具物料上架入库</field>
|
事项:完成刀具物料上架入库</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_tool_expired_remind" model="jikimo.message.template">
|
<record id="template_tool_expired_remind" model="jikimo.message.template">
|
||||||
<field name="menu_id" ref="mrp.menu_mrp_root"/>
|
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
|
||||||
<field name="action_id" ref="sf_tool_management.sf_functional_tool_dismantle_view_act"/>
|
<!-- <field name="action_id" ref="sf_tool_management.sf_functional_tool_dismantle_view_act"/>-->
|
||||||
<field name="name">功能刀具寿命到期</field>
|
<field name="name">功能刀具寿命到期</field>
|
||||||
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_dismantle"/>
|
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_dismantle"/>
|
||||||
<field name="model">sf.functional.tool.dismantle</field>
|
<field name="model">sf.functional.tool.dismantle</field>
|
||||||
@@ -89,13 +89,13 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 功能刀具寿命到期提醒:
|
<field name="content">### 功能刀具寿命到期提醒:
|
||||||
单号:拆解单[{{code}}]({{request_url}})
|
单号:拆解单[{{code}}]({{tool_expired_remind_special_url}})
|
||||||
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解</field>
|
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_tool_assembly_remind" model="jikimo.message.template">
|
<record id="template_tool_assembly_remind" model="jikimo.message.template">
|
||||||
<field name="menu_id" ref="mrp.menu_mrp_root"/>
|
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
|
||||||
<field name="action_id" ref="sf_tool_management.sf_functional_tool_assembly_view_act"/>
|
<!-- <field name="action_id" ref="sf_tool_management.sf_functional_tool_assembly_view_act"/>-->
|
||||||
<field name="name">功能刀具组装</field>
|
<field name="name">功能刀具组装</field>
|
||||||
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_assembly"/>
|
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_assembly"/>
|
||||||
<field name="model">sf.functional.tool.assembly</field>
|
<field name="model">sf.functional.tool.assembly</field>
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 功能刀具组装通知:
|
<field name="content">### 功能刀具组装通知:
|
||||||
单号:组装任务单[{{name}}]({{request_url}})
|
单号:组装任务单[{{name}}]({{tool_assembly_special_url}})
|
||||||
事项:{{use_tool_time}}前完成组装</field>
|
事项:{{use_tool_time}}前完成组装</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="template_production_completed_remind" model="jikimo.message.template">
|
<record id="template_production_completed_remind" model="jikimo.message.template">
|
||||||
|
|||||||
@@ -13,3 +13,14 @@ class SFMessagefunctionalToolAssembly(models.Model):
|
|||||||
if obj.loading_task_source == '0' and obj.assemble_status == '0':
|
if obj.loading_task_source == '0' and obj.assemble_status == '0':
|
||||||
obj.add_queue('功能刀具组装')
|
obj.add_queue('功能刀具组装')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_special_url(self,id,tmplate_name,special_name,model_id):
|
||||||
|
menu_id = 0
|
||||||
|
action_id = 0
|
||||||
|
if tmplate_name=='调拨入库' and special_name== 'tool_assembly_special_url':
|
||||||
|
menu_id = self.env.ref('mrp.menu_mrp_root').id
|
||||||
|
action_id = self.env.ref('sf_tool_management.sf_functional_tool_assembly_view_act').id
|
||||||
|
return super(SFMessagefunctionalToolAssembly, self).get_url(id, menu_id, action_id,model_id)
|
||||||
|
else:
|
||||||
|
return super(SFMessagefunctionalToolAssembly, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
@@ -17,3 +17,13 @@ class SFMessagefunctionalToolDismantle(models.Model):
|
|||||||
if obj.dismantle_cause in ['寿命到期报废', '崩刀报废'] and obj.state == '待拆解':
|
if obj.dismantle_cause in ['寿命到期报废', '崩刀报废'] and obj.state == '待拆解':
|
||||||
obj.add_queue('功能刀具寿命到期')
|
obj.add_queue('功能刀具寿命到期')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_special_url(self,id,tmplate_name,special_name,model_id):
|
||||||
|
menu_id = 0
|
||||||
|
action_id = 0
|
||||||
|
if tmplate_name=='调拨入库' and special_name== 'tool_expired_remind_special_url':
|
||||||
|
menu_id = self.env.ref('mrp.menu_mrp_root').id
|
||||||
|
action_id = self.env.ref('sf_tool_management.sf_functional_tool_dismantle_view_act').id
|
||||||
|
return super(SFMessagefunctionalToolDismantle, self).get_url(id, menu_id, action_id,model_id)
|
||||||
|
else:
|
||||||
|
return super(SFMessagefunctionalToolDismantle, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
@@ -73,7 +73,17 @@ class SFMessageStockPicking(models.Model):
|
|||||||
contents.append(content)
|
contents.append(content)
|
||||||
return contents
|
return contents
|
||||||
else:
|
else:
|
||||||
return self.deal_stock_picking_sfp(message_queue_ids)
|
res = super(SFMessageStockPicking, self)._get_message(message_queue_id)
|
||||||
|
return res
|
||||||
|
def get_special_url(self,id,tmplate_name,special_name,model_id):
|
||||||
|
menu_id = 0
|
||||||
|
action_id = 0
|
||||||
|
if tmplate_name=='调拨入库' and special_name== 'transfer_inventory_special_url':
|
||||||
|
menu_id = self.env.ref('stock.menu_stock_root').id
|
||||||
|
action_id = self.env.ref('stock.action_picking_tree_ready').id
|
||||||
|
return super(SFMessageStockPicking, self).get_url(id, menu_id, action_id,model_id)
|
||||||
|
else:
|
||||||
|
return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
|
|
||||||
def request_url(self):
|
def request_url(self):
|
||||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
|
|||||||
@@ -339,7 +339,7 @@
|
|||||||
name="空料架配送"
|
name="空料架配送"
|
||||||
sequence="11"
|
sequence="11"
|
||||||
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
|
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
|
||||||
groups="base.group_system"
|
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"
|
||||||
parent="mrp.menu_mrp_manufacturing"
|
parent="mrp.menu_mrp_manufacturing"
|
||||||
/>
|
/>
|
||||||
<!-- <menuitem -->
|
<!-- <menuitem -->
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
'summary': '智能工厂刀具管理',
|
'summary': '智能工厂刀具管理',
|
||||||
'sequence': 1,
|
'sequence': 1,
|
||||||
'description': """
|
'description': """
|
||||||
在本模块,定义了主要的角色、菜单、基础业务对象
|
在本模块,定义了刀具相关的模型和视图,以及相关的业务逻辑。
|
||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
|||||||
160
tool_service/装夹自动保存检测文件服务/app.py
Normal file
160
tool_service/装夹自动保存检测文件服务/app.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import os
|
||||||
|
from ftplib import FTP
|
||||||
|
import win32gui
|
||||||
|
import win32con
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 配置日志记录
|
||||||
|
logging.basicConfig(filename='service.log', level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class FileUploadRequest(BaseModel):
|
||||||
|
filename: str
|
||||||
|
|
||||||
|
|
||||||
|
# FTP 服务器配置信息
|
||||||
|
ftp_host = '110.52.114.162'
|
||||||
|
ftp_port = 10021
|
||||||
|
ftp_user = 'ftpuser'
|
||||||
|
ftp_password = '123456'
|
||||||
|
ftp_directory = '/home/ftp/ftp_root/ThreeTest/XT/Before/'
|
||||||
|
|
||||||
|
|
||||||
|
def find_child_window(parent_hwnd, class_name):
|
||||||
|
def enum_child_windows(hwnd, lparam):
|
||||||
|
class_name = win32gui.GetClassName(hwnd)
|
||||||
|
if class_name == lparam:
|
||||||
|
child_hwnds.append(hwnd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
child_hwnds = []
|
||||||
|
win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, class_name)
|
||||||
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
|
def find_child_window_by_partial_title(parent_hwnd, partial_title):
|
||||||
|
def enum_child_windows(hwnd, lparam):
|
||||||
|
# 获取窗口的标题
|
||||||
|
title = win32gui.GetWindowText(hwnd)
|
||||||
|
# 检查标题是否包含指定的部分标题
|
||||||
|
if partial_title in title:
|
||||||
|
child_hwnds.append(hwnd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
child_hwnds = []
|
||||||
|
win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, None)
|
||||||
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
|
def find_child_window_by_title(parent_hwnd, title):
|
||||||
|
def enum_child_windows(hwnd, lparam):
|
||||||
|
if win32gui.GetWindowText(hwnd) == lparam:
|
||||||
|
child_hwnds.append(hwnd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
child_hwnds = []
|
||||||
|
win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, title)
|
||||||
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
|
def set_path_and_save(filename):
|
||||||
|
parent_hwnd = win32gui.FindWindow(None, '另存为')
|
||||||
|
|
||||||
|
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)
|
||||||
|
logging.info(f"设置路径: {local_file_path}")
|
||||||
|
|
||||||
|
path_hwnds = find_child_window(parent_hwnd, 'Edit')
|
||||||
|
|
||||||
|
if not path_hwnds:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到路径框")
|
||||||
|
|
||||||
|
path_hwnd = path_hwnds[0]
|
||||||
|
win32gui.SendMessage(path_hwnd, win32con.WM_SETTEXT, 0, filename)
|
||||||
|
|
||||||
|
button_hwnds = find_child_window_by_title(parent_hwnd, '保存(&S)')
|
||||||
|
|
||||||
|
if not button_hwnds:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到保存按钮")
|
||||||
|
|
||||||
|
save_button_hwnd = button_hwnds[0]
|
||||||
|
win32gui.PostMessage(save_button_hwnd, win32con.BM_CLICK, 0, 0)
|
||||||
|
|
||||||
|
return local_file_path
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_file_to_save(filepath, timeout=30):
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
if os.path.isfile(filepath):
|
||||||
|
return True
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file_to_ftp(local_file):
|
||||||
|
|
||||||
|
if not os.path.isfile(local_file):
|
||||||
|
raise HTTPException(status_code=204, detail="文件未找到")
|
||||||
|
|
||||||
|
ftp = FTP()
|
||||||
|
try:
|
||||||
|
ftp.connect(ftp_host, ftp_port)
|
||||||
|
ftp.login(ftp_user, ftp_password)
|
||||||
|
ftp.cwd(ftp_directory)
|
||||||
|
with open(local_file, 'rb') as file:
|
||||||
|
ftp.storbinary(f'STOR {os.path.basename(local_file)}', file)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"文件上传失败: {e}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
ftp.quit()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/get/check/report")
|
||||||
|
async def upload_file(request: FileUploadRequest):
|
||||||
|
# 设置路径框并点击保存
|
||||||
|
local_file_path = set_path_and_save(request.filename)
|
||||||
|
logging.info(f"文件上传请求: {request.filename}")
|
||||||
|
logging.info(f"文件保存路径: {local_file_path}")
|
||||||
|
|
||||||
|
# 等待文件保存完成
|
||||||
|
if not wait_for_file_to_save(local_file_path):
|
||||||
|
raise HTTPException(status_code=500, detail="文件保存超时")
|
||||||
|
|
||||||
|
# 上传文件到 FTP
|
||||||
|
success = upload_file_to_ftp(local_file_path)
|
||||||
|
if success:
|
||||||
|
ftp_file_path = os.path.join(ftp_directory, request.filename)
|
||||||
|
return {"ftp_file_path": ftp_file_path}
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=500, detail="文件上传失败")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
5
tool_service/装夹自动保存检测文件服务/readme.md
Normal file
5
tool_service/装夹自动保存检测文件服务/readme.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
app.py是主程序文件,主要功能是监听装夹自动保存文件,并将其上传到FTP服务器。
|
||||||
|
|
||||||
|
需要讲app.py做成一个exe文件,可以用pyinstaller工具。
|
||||||
|
|
||||||
|
然后在windows的计划任务中执行此exe文件即可。
|
||||||
BIN
tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx
Normal file
BIN
tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user