设备增加网络配置,增加编程文件传输接口
This commit is contained in:
3
jikimo_work_reporting_api/__init__.py
Normal file
3
jikimo_work_reporting_api/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
18
jikimo_work_reporting_api/__manifest__.py
Normal file
18
jikimo_work_reporting_api/__manifest__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': '机企猫 报工系统API',
|
||||||
|
'version': '1.0.0',
|
||||||
|
'summary': """ 机企猫 报工系统API """,
|
||||||
|
'author': '机企猫',
|
||||||
|
'website': 'https://xt.sf.jikimo.com',
|
||||||
|
'category': 'sf',
|
||||||
|
'depends': ['base', 'sf_maintenance', 'jikimo_mini_program'],
|
||||||
|
'data': [
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
'application': True,
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
2
jikimo_work_reporting_api/controllers/__init__.py
Normal file
2
jikimo_work_reporting_api/controllers/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import main
|
||||||
42
jikimo_work_reporting_api/controllers/main.py
Normal file
42
jikimo_work_reporting_api/controllers/main.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import json
|
||||||
|
from odoo import http
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_nc_files
|
||||||
|
|
||||||
|
class MainController(http.Controller):
|
||||||
|
|
||||||
|
@http.route('/api/manual_download_program', type='json', methods=['POST'], auth='wechat_token', cors='*')
|
||||||
|
def work_reporting(self, **kwargs):
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
data = json.loads(request.httprequest.data)
|
||||||
|
maintenance_equipment_name = data.get('maintenance_equipment_name')
|
||||||
|
model_id = data.get('model_id')
|
||||||
|
if not maintenance_equipment_name or not model_id:
|
||||||
|
return {'code': 400, 'message': '参数错误'}
|
||||||
|
maintenance_equipment = request.env['maintenance.equipment'].sudo().search([('name', '=', maintenance_equipment_name)], limit=1)
|
||||||
|
if not maintenance_equipment:
|
||||||
|
return {'code': 400, 'message': '机床不存在'}
|
||||||
|
ftp_resconfig = request.env['res.config.settings'].sudo().get_values()
|
||||||
|
source_ftp_info = {
|
||||||
|
'host': ftp_resconfig['ftp_host'],
|
||||||
|
'port': int(ftp_resconfig['ftp_port']),
|
||||||
|
'username': ftp_resconfig['ftp_user'],
|
||||||
|
'password': ftp_resconfig['ftp_password']
|
||||||
|
}
|
||||||
|
target_ftp_info = {
|
||||||
|
'host': maintenance_equipment.ftp_host,
|
||||||
|
'port': int(maintenance_equipment.ftp_port),
|
||||||
|
'username': maintenance_equipment.ftp_username,
|
||||||
|
'password': maintenance_equipment.ftp_password
|
||||||
|
}
|
||||||
|
# 传输nc文件
|
||||||
|
if transfer_nc_files(
|
||||||
|
source_ftp_info,
|
||||||
|
target_ftp_info,
|
||||||
|
'/' + str(model_id),
|
||||||
|
'/home/jikimo/testdir'):
|
||||||
|
return {'code': 200, 'message': 'success'}
|
||||||
|
else:
|
||||||
|
return {'code': 500, 'message': '传输失败'}
|
||||||
1
jikimo_work_reporting_api/models/__init__.py
Normal file
1
jikimo_work_reporting_api/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import logging
|
import logging
|
||||||
from ftplib import FTP
|
import os
|
||||||
|
from ftplib import FTP, error_perm
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -52,8 +53,8 @@ class FtpController:
|
|||||||
print(self.username, self.port, self.host, self.password)
|
print(self.username, self.port, self.host, self.password)
|
||||||
ftp = FTP_P()
|
ftp = FTP_P()
|
||||||
_logger.info("===================connect==================")
|
_logger.info("===================connect==================")
|
||||||
# self.ftp.set_debuglevel(2) #打开调试级别2,显示详细信息
|
# ftp.set_debuglevel(2) #打开调试级别2,显示详细信息
|
||||||
ftp.set_pasv(0) # 0主动模式 1 #被动模式
|
# ftp.set_pasv(1) # 0主动模式 1 #被动模式
|
||||||
try:
|
try:
|
||||||
ftp.connect(self.host, self.port)
|
ftp.connect(self.host, self.port)
|
||||||
ftp.login(self.username, self.password)
|
ftp.login(self.username, self.password)
|
||||||
@@ -128,3 +129,126 @@ class FtpController:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
self.ftp.delete(delpath)
|
self.ftp.delete(delpath)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_nc_files(source_ftp_info, target_ftp_info, source_dir, target_dir, keep_dir=False):
|
||||||
|
"""
|
||||||
|
从源FTP服务器下载所有.nc文件并上传到目标FTP服务器,保持目录结构
|
||||||
|
|
||||||
|
Args:
|
||||||
|
source_ftp_info: dict, 源FTP连接信息 {host, port, username, password}
|
||||||
|
target_ftp_info: dict, 目标FTP连接信息 {host, port, username, password}
|
||||||
|
source_dir: str, 源FTP上的起始目录
|
||||||
|
target_dir: str, 目标FTP上的目标目录
|
||||||
|
keep_dir: bool, 是否保持目录结构,默认False
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 连接源FTP
|
||||||
|
source_ftp = FtpController(
|
||||||
|
source_ftp_info['host'],
|
||||||
|
source_ftp_info['port'],
|
||||||
|
source_ftp_info['username'],
|
||||||
|
source_ftp_info['password']
|
||||||
|
)
|
||||||
|
source_ftp.ftp.set_pasv(1)
|
||||||
|
|
||||||
|
# 连接目标FTP
|
||||||
|
target_ftp = FtpController(
|
||||||
|
target_ftp_info['host'],
|
||||||
|
target_ftp_info['port'],
|
||||||
|
target_ftp_info['username'],
|
||||||
|
target_ftp_info['password']
|
||||||
|
)
|
||||||
|
source_ftp.ftp.set_pasv(1)
|
||||||
|
|
||||||
|
# 递归遍历源目录
|
||||||
|
def traverse_dir(current_dir, relative_path=''):
|
||||||
|
source_ftp.ftp.cwd(current_dir)
|
||||||
|
file_list = source_ftp.ftp.nlst()
|
||||||
|
|
||||||
|
for item in file_list:
|
||||||
|
try:
|
||||||
|
# 尝试进入目录
|
||||||
|
source_ftp.ftp.cwd(f"{current_dir}/{item}")
|
||||||
|
# 如果成功则是目录
|
||||||
|
new_relative_path = os.path.join(relative_path, item)
|
||||||
|
# 在目标FTP创建对应目录
|
||||||
|
try:
|
||||||
|
if keep_dir:
|
||||||
|
target_ftp.ftp.mkd(f"{target_dir}/{new_relative_path}")
|
||||||
|
except:
|
||||||
|
pass # 目录可能已存在
|
||||||
|
# 递归遍历子目录
|
||||||
|
traverse_dir(f"{current_dir}/{item}", new_relative_path)
|
||||||
|
source_ftp.ftp.cwd('..')
|
||||||
|
except:
|
||||||
|
# 如果是.nc文件则传输
|
||||||
|
if item.lower().endswith('.nc'):
|
||||||
|
# 下载到临时文件
|
||||||
|
temp_path = f"/tmp/{item}"
|
||||||
|
with open(temp_path, 'wb') as f:
|
||||||
|
source_ftp.ftp.retrbinary(f'RETR {item}', f.write)
|
||||||
|
|
||||||
|
# 上传到目标FTP对应目录
|
||||||
|
if keep_dir:
|
||||||
|
target_path = f"{target_dir}/{relative_path}/{item}"
|
||||||
|
else:
|
||||||
|
target_path = f"{target_dir}/{item}"
|
||||||
|
with open(temp_path, 'rb') as f:
|
||||||
|
target_ftp.ftp.storbinary(f'STOR {target_path}', f)
|
||||||
|
|
||||||
|
# 删除临时文件
|
||||||
|
os.remove(temp_path)
|
||||||
|
logging.info(f"已传输文件: {item}")
|
||||||
|
|
||||||
|
# 清空目标目录下的所有内容
|
||||||
|
try:
|
||||||
|
target_ftp.ftp.cwd(target_dir)
|
||||||
|
files = target_ftp.ftp.nlst()
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
try:
|
||||||
|
# 尝试删除文件
|
||||||
|
target_ftp.ftp.delete(f)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
# 如果删除失败,可能是目录,递归删除目录
|
||||||
|
def remove_dir(path):
|
||||||
|
target_ftp.ftp.cwd(path)
|
||||||
|
sub_files = target_ftp.ftp.nlst()
|
||||||
|
for sf in sub_files:
|
||||||
|
try:
|
||||||
|
target_ftp.ftp.delete(sf)
|
||||||
|
except:
|
||||||
|
remove_dir(f"{path}/{sf}")
|
||||||
|
target_ftp.ftp.cwd('..')
|
||||||
|
target_ftp.ftp.rmd(path)
|
||||||
|
|
||||||
|
remove_dir(f"{target_dir}/{f}")
|
||||||
|
except:
|
||||||
|
logging.error(f"无法删除 {f}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
logging.info(f"已清空目标目录 {target_dir}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"清空目标目录失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 开始遍历
|
||||||
|
traverse_dir(source_dir)
|
||||||
|
|
||||||
|
logging.info("所有.nc文件传输完成")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"传输过程出错: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# 关闭FTP连接
|
||||||
|
try:
|
||||||
|
source_ftp.ftp.quit()
|
||||||
|
target_ftp.ftp.quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
@@ -4,3 +4,4 @@ from . import sf_maintenance_oee
|
|||||||
from . import sf_maintenance_logs
|
from . import sf_maintenance_logs
|
||||||
from . import sf_equipment_maintenance_standards
|
from . import sf_equipment_maintenance_standards
|
||||||
from . import sf_maintenance_requests
|
from . import sf_maintenance_requests
|
||||||
|
from . import maintenance_qrcode_printer
|
||||||
|
|||||||
92
sf_maintenance/models/maintenance_qrcode_printer.py
Normal file
92
sf_maintenance/models/maintenance_qrcode_printer.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import qrcode
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class MaintenanceEquipment(models.Model):
|
||||||
|
_name = 'maintenance.equipment'
|
||||||
|
_inherit = ['maintenance.equipment', 'printing.utils']
|
||||||
|
|
||||||
|
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
|
||||||
|
|
||||||
|
@api.depends('name')
|
||||||
|
def _generate_qr_code(self):
|
||||||
|
for record in self:
|
||||||
|
# Generate QR code
|
||||||
|
qr = qrcode.QRCode(
|
||||||
|
version=1,
|
||||||
|
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
||||||
|
box_size=10,
|
||||||
|
border=4,
|
||||||
|
)
|
||||||
|
qr.add_data(record.name)
|
||||||
|
qr.make(fit=True)
|
||||||
|
qr_image = qr.make_image(fill_color="black", back_color="white")
|
||||||
|
|
||||||
|
# Encode the image data in base64
|
||||||
|
image_stream = BytesIO()
|
||||||
|
qr_image.save(image_stream, format="PNG")
|
||||||
|
encoded_image = base64.b64encode(image_stream.getvalue())
|
||||||
|
|
||||||
|
record.qr_code_image = encoded_image
|
||||||
|
|
||||||
|
def print_single_method(self):
|
||||||
|
|
||||||
|
print('self.name========== %s' % self.name)
|
||||||
|
self.ensure_one()
|
||||||
|
qr_code_data = self.qr_code_image
|
||||||
|
if not qr_code_data:
|
||||||
|
raise UserError("没有找到二维码数据。")
|
||||||
|
maintenance_equipment_name = self.name
|
||||||
|
# host = "192.168.50.110" # 可以根据实际情况修改
|
||||||
|
# port = 9100 # 可以根据实际情况修改
|
||||||
|
|
||||||
|
# 获取默认打印机配置
|
||||||
|
printer_config = self.env['printer.configuration'].sudo().search([('model', '=', '机床二维码')], limit=1)
|
||||||
|
if not printer_config:
|
||||||
|
raise UserError('请先配置打印机')
|
||||||
|
host = printer_config.printer_id.ip_address
|
||||||
|
port = printer_config.printer_id.port
|
||||||
|
self.print_qr_code(maintenance_equipment_name, host, port)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_zpl_code(self, code):
|
||||||
|
"""生成ZPL代码用于打印二维码标签
|
||||||
|
Args:
|
||||||
|
code: 需要编码的内容
|
||||||
|
Returns:
|
||||||
|
str: ZPL指令字符串
|
||||||
|
"""
|
||||||
|
zpl_code = "^XA\n" # 开始ZPL格式
|
||||||
|
|
||||||
|
# 设置打印参数
|
||||||
|
zpl_code += "^LH0,0\n" # 设置标签起始位置
|
||||||
|
zpl_code += "^CI28\n" # 设置中文编码
|
||||||
|
zpl_code += "^PW400\n" # 设置打印宽度为400点
|
||||||
|
zpl_code += "^LL300\n" # 设置标签长度为300点
|
||||||
|
|
||||||
|
# 打印标题
|
||||||
|
zpl_code += "^FO10,20\n" # 设置标题位置
|
||||||
|
zpl_code += "^A0N,30,30\n" # 设置字体大小
|
||||||
|
zpl_code += "^FD机床二维码^FS\n" # 打印标题文本
|
||||||
|
|
||||||
|
# 打印二维码
|
||||||
|
zpl_code += "^FO50,60\n" # 设置二维码位置
|
||||||
|
zpl_code += f"^BQN,2,8\n" # 设置二维码参数:模式2,放大倍数8
|
||||||
|
zpl_code += f"^FDLA,{code}^FS\n" # 二维码内容
|
||||||
|
|
||||||
|
# 打印编码文本
|
||||||
|
zpl_code += "^FO50,220\n" # 设置编码文本位置
|
||||||
|
zpl_code += "^A0N,25,25\n" # 设置字体大小
|
||||||
|
zpl_code += f"^FD编码: {code}^FS\n" # 打印编码文本
|
||||||
|
|
||||||
|
# 打印日期
|
||||||
|
zpl_code += "^FO50,260\n"
|
||||||
|
zpl_code += "^A0N,20,20\n"
|
||||||
|
zpl_code += f"^FD打印日期: {fields.Date.today()}^FS\n"
|
||||||
|
|
||||||
|
zpl_code += "^PQ1\n" # 打印1份
|
||||||
|
zpl_code += "^XZ\n" # 结束ZPL格式
|
||||||
|
|
||||||
|
return zpl_code
|
||||||
|
|
||||||
@@ -826,6 +826,11 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
image_lq_id = fields.Many2many('maintenance.equipment.image', 'equipment_lq_id', string='冷却方式',
|
image_lq_id = fields.Many2many('maintenance.equipment.image', 'equipment_lq_id', string='冷却方式',
|
||||||
domain="[('type', '=', '冷却方式')]")
|
domain="[('type', '=', '冷却方式')]")
|
||||||
|
|
||||||
|
ftp_host = fields.Char('FTP 主机')
|
||||||
|
ftp_port = fields.Char('FTP 端口')
|
||||||
|
ftp_username = fields.Char('FTP 用户名')
|
||||||
|
ftp_password = fields.Char('FTP 密码')
|
||||||
|
|
||||||
|
|
||||||
class SfRobotAxisNum(models.Model):
|
class SfRobotAxisNum(models.Model):
|
||||||
_name = 'sf.robot.axis.num'
|
_name = 'sf.robot.axis.num'
|
||||||
|
|||||||
@@ -1053,6 +1053,26 @@
|
|||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//group/field[@name='location']" position="after">
|
||||||
|
<field name="qr_code_image" widget="image" readonly="1" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" />
|
||||||
|
<label for="print_single_method"/>
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
|
||||||
|
<button type="object" class="oe_highlight" name='print_single_method' string="打印机床二维码"
|
||||||
|
attrs="{'invisible': [('equipment_type', '!=', '机床')]}"/>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='maintenance']" position="after">
|
||||||
|
<page name="network_config" string="网络配置" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" >
|
||||||
|
<group>
|
||||||
|
<group string="ftp配置">
|
||||||
|
<field name="ftp_host" string="主机"/>
|
||||||
|
<field name="ftp_port" string="端口"/>
|
||||||
|
<field name="ftp_username" string="用户名"/>
|
||||||
|
<field name="ftp_password" string="密码" password="True"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
</data>
|
</data>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
Reference in New Issue
Block a user