KWD 7 месяцев назад
Родитель
Сommit
afba586ab3

+ 389 - 0
TeamAAS-VM/Core/FtpUploader.cs

@@ -0,0 +1,389 @@
+using System.IO.Compression;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TeamAAS_VP.Models;
+
+namespace TeamAAS_VP.Core
+{
+    public class FtpUploader
+    {
+        //private readonly Timer _timer;
+        //private static readonly Timer _cleanupTimer = new Timer();
+        private ConfigFtp _config;
+        public FtpUploader()
+        {
+            try
+            {
+                string configFilePath = "..//Config//FtpConfig.cfg";
+                //加载配置文件
+                _config = new ConfigFtp();
+                _config = LoadConfiguration(configFilePath);
+                //设置上传定时器
+                //_timer = new Timer(_config.IntervalMinutes * 60 * 1000);
+                //_timer.Elapsed += (s, e) => UploadAllFolders();
+                //_timer.AutoReset = true;
+
+                //// 设置清理定时器
+                //_cleanupTimer.Interval = _config.CleanupIntervalMinutes * 60 * 1000;
+                //_cleanupTimer.Elapsed += (sender, e) => CleanupOldFilesOnFtp();
+                //_cleanupTimer.AutoReset = true;
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+        public void Start()
+        {
+            if (_config.Open)
+            {
+                Task.Run(async () =>
+                {
+                    while (true)
+                    {
+                        try
+                        {
+                            //_timer.Start();
+                            //SendMessage.SendInfo("启动Ftp上传服务...", Color.Black);
+                            //立即执行一次
+                            UploadAllFolders();
+                        }
+                        catch (Exception ex)
+                        {
+
+                        }
+                        finally
+                        {
+                            await Task.Delay(_config.IntervalMinutes * 60 * 1000);
+                        }
+                    }
+                });
+            }
+        }
+        public void Stop()
+        {
+            //_timer.Stop();
+            //SendMessage.SendInfo("Ftp上传服务已停止", Color.Black);
+        }
+        private ConfigFtp LoadConfiguration(string configFilePath)
+        {
+            try
+            {
+                string json = File.ReadAllText(configFilePath);
+                return JsonConvert.DeserializeObject<ConfigFtp>(json);
+            }
+            catch (Exception ex)
+            {
+                //SendMessage.SendInfo($"加载配置文件失败:{ex.Message}", Color.Red);
+                return null;
+            }
+        }
+        /// <summary>
+        /// 开始上传文件
+        /// </summary>
+        public void UploadAllFolders()
+        {
+            Task.Run(() =>
+            {
+                foreach (var localFolder in _config.LocalFolders)
+                {
+                    try
+                    {
+                        if (!Directory.Exists(localFolder))
+                        {
+                            //文件夹不存在
+                            continue;
+                        }
+                        var files = Directory.GetFiles(localFolder).ToList();
+                        // if (!files.Any())
+                        // {
+                        //string path1 = localFolder + $"\\{DateTime.Now.ToString("yyyy-MM")}";
+                        string path2 = localFolder + $"\\{DateTime.Now.ToString("yyyy-MM")}\\{DateTime.Now.ToString("MM-dd")}\\OK";
+                        string path3 = localFolder + $"\\{DateTime.Now.ToString("yyyy-MM")}\\{DateTime.Now.ToString("MM-dd")}\\NG";
+                        string path4 = localFolder;
+                        //if (Directory.Exists(path1))
+                        //{
+                        //    files = Directory.GetFiles(path1).ToList();
+                        //    string str1 = string.Join("/", path1.Split('\\').Skip(2));
+                        //    EnsureFtpDirectoryExists(str1);
+                        //}
+                        if (Directory.Exists(path2))
+                        {
+                            var files1 = Directory.GetFiles(path2).ToList();
+                            if (Directory.Exists(path3))
+                            {
+                                var files2 = Directory.GetFiles(path3).ToList();
+                                foreach (var item in files2)
+                                {
+                                    files1.Add(item);
+                                }
+                            }
+                            files = files1;
+                            string str2 = string.Join("/", path2.Split('\\').Skip(2));
+                            EnsureFtpDirectoryExists(str2);
+                            string str3 = string.Join("/", path3.Split('\\').Skip(2));
+                            EnsureFtpDirectoryExists(str3);
+                        }
+                        else if (Directory.Exists(path3))
+                        {
+                            var files1 = Directory.GetFiles(path3).ToList();
+                            files = files1;
+                            string str3 = string.Join("/", path3.Split('\\').Skip(2));
+                            EnsureFtpDirectoryExists(str3);
+                        }
+                        else if (Directory.Exists(path4))
+                        {
+                            files = Directory.GetFiles(path4).ToList();
+                            string str4 = string.Join("/", path4.Split('\\').Skip(2));
+                            EnsureFtpDirectoryExists(str4);
+                        }
+                        else
+                        {
+                            // SendMessage.SendInfo($"没有找到可上传文件:{localFolder}", Color.Black);
+                            continue;
+                        }
+                        // }
+
+                        foreach (var file in files)
+                        {
+                            UploadFile(file);
+                            //CleanupOldFilesOnFtp(file);
+                        }
+                        files.Clear();
+                        Thread.Sleep(500);
+                    }
+                    catch (Exception ex)
+                    {
+                        LogHelper.WriteLogError($"处理文件夹{localFolder}时出错", ex);
+                    }
+                }
+            });
+        }
+        private void UploadFile(string localFilePath)
+        {
+            if (_config.Open)
+            {
+                string str = string.Join("/", localFilePath.Split('\\').Skip(2));
+                string fileName = Path.GetFileName(localFilePath);
+                string ftpUri = $"{_config.FtpServer}/{_config.RemotePath}/{str}";
+                try
+                {
+                    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
+                    request.Method = WebRequestMethods.Ftp.UploadFile;
+                    request.Credentials = new NetworkCredential(_config.Username, _config.Password);
+                    using (var fileStream = File.OpenRead(localFilePath))
+                    using (Stream ftpStream = request.GetRequestStream())
+                    {
+                        fileStream.CopyTo(ftpStream);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError($"上传{fileName}失败", ex);
+                }
+            }
+        }
+        private void EnsureFtpDirectoryExists(string ftpSubDirectory)
+        {
+            if (_config.Open)
+            {
+                string ftpUri = $"{_config.FtpServer}/{_config.RemotePath}/{ftpSubDirectory}";
+                try
+                {
+                    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
+                    request.Method = WebRequestMethods.Ftp.MakeDirectory;
+                    request.Credentials = new NetworkCredential(_config.Username, _config.Password);
+                    using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
+                    {
+                        //SendMessage.SendInfo($"创建Ftp目录:{ftpSubDirectory}", Color.Black);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    //SendMessage.SendInfo($"创建目录{ftpSubDirectory}失败:{ex.Message}", Color.Red);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 上传文件到ftp
+        /// </summary>
+        /// <param name="path"></param>
+        public void UploadToFtp(string path)
+        {
+            if (_config.Open)
+            {
+                try
+                {
+                    Task.Run(() =>
+                    {
+                        List<string> files = new List<string>();
+                        if (Directory.Exists(path))
+                        {
+                            files = Directory.GetFiles(path).ToList();
+                            string str = string.Join("/", path.Split('\\').Skip(2));
+                            EnsureFtpDirectoryExists(str);
+                        }
+
+                        foreach (var file in files)
+                        {
+                            UploadFile(file);
+                        }
+                        files.Clear();
+                    });
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError($"上传FTP文件{path}时出错", ex);
+                }
+            }
+        }
+
+        #region 清理
+        public void CleanupOldFilesOnFtp(string localFilePath)
+        {
+            //Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - 开始执行FTP文件清理任务");
+            var cutoffDate = DateTime.Now.AddDays(-_config.DeleteDay);
+
+            //foreach (var folder in _config.LocalFolders)
+            //{
+            //string remoteDir = string.Join("/", localFilePath.Split('\\').Skip(2));
+            string fileName = Path.GetFileName(localFilePath);
+            //var remoteDir = $"{_config.RemotePath}{folderName}/";
+            string remoteDir = "";
+            ////截取地址中前两个\\保留后面内容
+            //int firstSlashIndex = folder.IndexOf('\\'); 
+            //int secondSlashIndex = folder.IndexOf('\\', firstSlashIndex + 1); 
+            //if (secondSlashIndex >= 0)
+            //{
+            //    string result = folder.Substring(secondSlashIndex + 1);
+            //    remoteDir = $"{_config.FtpServer}/{_config.RemotePath}/{result}";
+            //}
+
+            //去掉前两个//,并且去掉后面文件名和格式
+            string[] parts = localFilePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+            if (parts.Length >= 5) // 确保路径足够长
+            {
+                var middleParts = parts.Skip(3).Take(parts.Length - 4); // 跳过前3部分,去掉最后1部分
+                string result = string.Join("/", middleParts); // 合并成路径
+                remoteDir = result;
+            }
+            try
+            {
+                //获取FTP服务器指定目录下的文件列表,并将结果存储在files变量中。
+                var files = ListFilesOnFtp(remoteDir);
+
+                foreach (var file in files)
+                {
+                    if (file.LastModified < cutoffDate)
+                    {
+                        DeleteFileOnFtp(remoteDir + file.Name);
+                        //Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - 已删除FTP旧文件: {remoteDir}{file.Name} (修改时间: {file.LastModified})");
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - 清理FTP目录 {remoteDir} 出错", ex);
+            }
+            //}
+            //Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - FTP文件清理任务完成");
+        }
+
+        public List<FtpFileInfo> ListFilesOnFtp(string remoteDir)
+        {
+            var files = new List<FtpFileInfo>();
+            var uri = new Uri($"{_config.FtpServer}/{_config.RemotePath}/{remoteDir}");//Uri($"{_config.FtpServer}/{remoteDir}");
+
+            var request = (FtpWebRequest)WebRequest.Create(uri);
+            request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
+            request.Credentials = new NetworkCredential(_config.Username, _config.Password);
+            request.UsePassive = true;
+
+            using (var response = (FtpWebResponse)request.GetResponse())
+            using (var stream = response.GetResponseStream())
+            using (var reader = new StreamReader(stream))
+            {
+                string line;
+                while ((line = reader.ReadLine()) != null)
+                {
+                    var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+                    if (parts.Length > 8 && !string.IsNullOrEmpty(parts[8]))
+                    {
+                        try
+                        {
+                            var dateStr = $"{parts[5]} {parts[6]} {parts[7]}";
+                            if (DateTime.TryParse(dateStr, out var lastModified))
+                            {
+                                files.Add(new FtpFileInfo
+                                {
+                                    Name = parts[8],
+                                    LastModified = lastModified
+                                });
+                            }
+                        }
+                        catch
+                        {
+                            // 忽略解析错误的行
+                        }
+                    }
+                }
+            }
+
+            return files;
+        }
+
+        public void DeleteFileOnFtp(string remoteFilePath)
+        {
+            var uri = new Uri($"{_config.FtpServer}/{_config.RemotePath}/{remoteFilePath}");
+
+            var request = (FtpWebRequest)WebRequest.Create(uri);
+            request.Method = WebRequestMethods.Ftp.DeleteFile;
+            request.Credentials = new NetworkCredential(_config.Username, _config.Password);
+            request.UsePassive = true;
+
+            using (var response = (FtpWebResponse)request.GetResponse())
+            {
+                if (response.StatusCode != FtpStatusCode.FileActionOK)
+                {
+                    // throw new Exception($"删除文件失败: {response.StatusCode}");
+                }
+            }
+        }
+        #endregion
+
+        #region 压缩图片文件夹
+
+        public string CompressionImage(string name)
+        {
+            if (_config.Open)
+            {
+                try
+                {
+                    string zipPath = "";
+                    Task.Run(() =>
+                    {
+                        zipPath = $"{_config.ImagePath}\\{name}.zip";//生成的zip文件路径
+                        ZipFile.CreateFromDirectory(_config.ImagePath, zipPath);
+                    });
+                    return zipPath;
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError($"压缩图片文件时出错", ex);
+                    return "";
+                }
+            }
+            return "";
+        }
+
+        #endregion
+    }
+}

+ 22 - 0
TeamAAS-VM/Core/Management.cs

@@ -237,6 +237,8 @@ namespace TeamAAS_VP.Core
             get { return _dfc; }
             set { SetProperty(ref _dfc, value); }
         }
+
+        public FtpUploader ftpService { get; set; } = new FtpUploader();
         #endregion
 
         #region 命令
@@ -2404,6 +2406,26 @@ namespace TeamAAS_VP.Core
                                                 if (recordResult)
                                                 {
                                                     SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存成功", MessageLevel.Debug);
+
+                                                    ftpService.UploadToFtp(stationConfig.SaveCsvPath);
+                                                    if (i==0)
+                                                    {
+                                                        string name = $"{ProductMainSN[i]}_{DateTime.Now.ToString("yyyyMMddHHmmss")}";
+                                                        string zippath = ftpService.CompressionImage(name);
+                                                        ftpService.UploadToFtp(zippath);
+
+                                                        (bool isSuc, string resp) = await _mesService.SendFtpFileAsync(ProductMainSN[i], name);
+                                                        if (isSuc)
+                                                        {
+                                                            LogHelper.WriteLogMes($"上传ftp成功");
+                                                            SendTaskMessage($"上传ftp成功", MessageLevel.Debug);
+                                                        }
+                                                        else
+                                                        {
+                                                            LogHelper.WriteLogMes($"上传ftp失败");
+                                                            SendTaskMessage($"上传ftp失败", MessageLevel.Error);
+                                                        }
+                                                    }
                                                 }
                                                 else
                                                 {

+ 9 - 0
TeamAAS-VM/Interfaces/IMesService.cs

@@ -139,5 +139,14 @@ namespace TeamAAS_VP.Interfaces
         /// <param name="timeoutInSeconds"></param>
         /// <returns></returns>
         Task<(bool IsSuccess, DateTime Now)> GetServerTimeAsync(int deviceNo, string sn, double timeoutInSeconds);
+
+        /// <summary>
+        /// ÉÏ´«ftp
+        /// </summary>
+        /// <param name="sn"></param>
+        /// <param name="name"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<(bool IsSuccess, string Response)> SendFtpFileAsync(string sn, string name, CancellationToken cancellationToken = default);
     }
 }

+ 27 - 0
TeamAAS-VM/Models/ConfigFtp.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TeamAAS_VP.Models
+{
+    public class ConfigFtp
+    {
+        public bool Open { get; set; }
+        public string FtpServer { get; set; }
+        public string Username { get; set; }
+        public string Password { get; set; }
+        public string RemotePath { get; set; }
+        public int IntervalMinutes { get; set; }
+        public int DeleteDay { get; set; }
+        public List<string> LocalFolders { get; set; }//要监控的本地文件夹列表
+        public string ImagePath { get; set; }//图片路径
+    }
+
+    public class FtpFileInfo
+    {
+        public string Name { get; set; }
+        public DateTime LastModified { get; set; }
+    }
+}

+ 66 - 6
TeamAAS-VM/Services/MesService.cs

@@ -662,17 +662,17 @@ namespace TeamAAS_VP.Services
                 device = _configService.GetDeviceInfo();
             if (device == null || !device.EnableMES)
             {
-                return (true, DateTime.UtcNow);
+                return (true, DateTime.Now);
             }
             if (string.IsNullOrWhiteSpace(device.MesUrl))
-                return (false, DateTime.UtcNow);
+                return (false, DateTime.Now);
 
             string url = "";
             url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=mes_server_time";
 
             LogHelper.WriteLogMes($"【询问URL】{url}");
             SendTaskMessage($"【询问URL】{url}", MessageLevel.Info);
-            (bool IsSuccess, DateTime Now) result = (false, DateTime.UtcNow);
+            (bool IsSuccess, DateTime Now) result = (false, DateTime.Now);
 
             for (int i = 0; i < 3; i++)
             {
@@ -701,21 +701,81 @@ namespace TeamAAS_VP.Services
                                 }
                             }
                         }
-                        result = (false, DateTime.UtcNow);
+                        result = (false, DateTime.Now);
                     }
                 }
                 catch (OperationCanceledException)
                 {
-                    result = (false, DateTime.UtcNow);
+                    result = (false, DateTime.Now);
                 }
                 catch (Exception ex)
                 {
-                    result = (false, DateTime.UtcNow);
+                    result = (false, DateTime.Now);
                 }
             }
             return result;
         }
 
+        /// <summary>
+        /// 上传ftp
+        /// </summary>
+        /// <param name="sn"></param>
+        /// <param name="name"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="ObjectDisposedException"></exception>
+        public async Task<(bool IsSuccess, string Response)> SendFtpFileAsync(string sn,string name,CancellationToken cancellationToken = default)
+        {
+            // 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
+            if (_disposed) throw new ObjectDisposedException(nameof(MesService));
+
+            var device = _configService.GetDeviceInfo();
+            if (device == null || !device.EnableMES)
+            {
+                return (true, "MES功能未启用!");
+            }
+           
+            string url = $"http://127.0.0.1/api.ashx?db=mysql&ApiKey=123!#yuiop&jsondata={{\"SN\":\"{sn}\",\"LINE\":\"{device.Line}\",\"STATION\":\"{device.Station}\",\"TESTRESULT\":\"PASS\",\"FIXTUREID\":\"{device.FixtureId}\",\"TESTDATETIME\":\"{DateTime.Now.ToString()}\",\"LOGFILENAME\":\"{name}.zip\"}}";
+           
+            LogHelper.WriteLogMes($"【上传FTP的URL】{url}");
+            SendTaskMessage($"【上传FTP的URL】{url}", MessageLevel.Info);
+            (bool IsSuccess, string Response) result = (false, string.Empty);
+
+            for (int i = 0; i < 3; i++)
+            {
+                try
+                {
+                    using (var rsp = await _httpClient.GetAsync(url, cancellationToken))
+                    {
+                        var text = await rsp.Content.ReadAsStringAsync();
+                        LogHelper.WriteLogMes($"【HTTP接收】{text}");
+                        SendTaskMessage($"【HTTP接收】{text}", MessageLevel.Info);
+                        // "SFC_OK" 代表和 MES 连接成功
+                        if (text.Contains("SFC_OK"))
+                        {
+                            // 如果有附加信息(多行),代表此 SN 数据有问题,视为失败并返回该文本
+                            if (text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Length > 1)
+                            {
+                                return (false, text);
+                            }
+                            return (true, text);
+                        }
+                        result = (false, text);
+                    }
+                }
+                catch (OperationCanceledException)
+                {
+                    result = (false, "Canceled");
+                }
+                catch (Exception ex)
+                {
+                    result = (false, ex.Message);
+                }
+            }
+            return result;
+        }
+
+
         /// <summary>
         /// 释放服务占用的托管资源(当前仅 <see cref="_httpClient"/>)。
         /// <para>本方法为幂等操作,重复调用不会产生异常。调用后实例不可再用于发起请求,尝试使用将抛出 <see cref="ObjectDisposedException"/>。</para>

+ 4 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -363,6 +363,8 @@
     <Reference Include="System.Formats.Asn1, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
       <HintPath>..\packages\System.Formats.Asn1.9.0.9\lib\net462\System.Formats.Asn1.dll</HintPath>
     </Reference>
+    <Reference Include="System.IO.Compression" />
+    <Reference Include="System.IO.Compression.FileSystem" />
     <Reference Include="System.IO.Pipelines, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
       <HintPath>..\packages\System.IO.Pipelines.9.0.9\lib\net462\System.IO.Pipelines.dll</HintPath>
     </Reference>
@@ -530,12 +532,14 @@
       <DependentUpon>FeederSystemParam.xaml</DependentUpon>
     </Compile>
     <Compile Include="Core\FileNameTemplate.cs" />
+    <Compile Include="Core\FtpUploader.cs" />
     <Compile Include="Core\StabilityAnalyzer.cs" />
     <Compile Include="Core\VisionProFileHelper.cs" />
     <Compile Include="Events\MainTabSwitchNotification.cs" />
     <Compile Include="Events\WorkShiftChangedNotification.cs" />
     <Compile Include="Events\WorkShiftUpdateNotification.cs" />
     <Compile Include="Interfaces\IMesService.cs" />
+    <Compile Include="Models\ConfigFtp.cs" />
     <Compile Include="Models\CTQ_Data.cs" />
     <Compile Include="Models\DataFormCtq.cs" />
     <Compile Include="Models\DeviceInfo.cs" />

+ 1 - 1
TeamAAS-VM/ViewModels/Statistics/ProductionStatementViewModel.cs

@@ -214,7 +214,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
                 MajorGridlineColor = OxyColor.Parse("#F3F3F3"),
             };
 
-            var now = DateTime.UtcNow;
+            var now = DateTime.Now;
             var monthStart = DateTimeAxis.ToDouble(new DateTime(now.Year, now.Month, 1));
             var temp = new DateTime(now.Year, now.Month, 1);
             var monthEnd = DateTimeAxis.ToDouble(temp.AddMonths(1).AddDays(-1));

+ 4 - 0
TeamAAS-VM/Views/Home/ShowVisionRender.xaml.cs

@@ -549,6 +549,10 @@ namespace TeamAAS_VP.Views.Home
                         {
                             reimage.Save(Recordedpath);
                         }
+                        ////================
+                        //FtpUploader ftpService = new FtpUploader();
+                        //string zippath = ftpService.CompressionImage("123456789");
+                        //ftpService.UploadToFtp(zippath);
                     }
                 }
                 catch (Exception ex)