KWD 6 maanden geleden
bovenliggende
commit
6798e4bb59

+ 1 - 1
TeamAAS-VM/Core/FileNameTemplate.cs

@@ -90,7 +90,7 @@ namespace TeamAAS_VP.Core
             // 日期时间变量
             RegisterVariable("Now", () => DateTime.Now);
             RegisterVariable("Today", () => DateTime.Today);
-            RegisterVariable("UtcNow", () => DateTime.UtcNow);
+            RegisterVariable("Now", () => DateTime.Now);
 
             // 可以在这里添加更多内置变量
         }

+ 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
+    }
+}

+ 24 - 2
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 命令
@@ -2138,7 +2140,7 @@ namespace TeamAAS_VP.Core
                                             SendTaskMessage($"收到{i}过站检查请求(当站是否生产)...", MessageLevel.Debug);
                                         }
 
-                                        _LastMesCheckTime[i] = DateTime.UtcNow;
+                                        _LastMesCheckTime[i] = DateTime.Now;
                                         LogHelper.WriteLogMes($"【mes开始QUERY】");
                                         SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
                                         if (systemConfig.IsBasePartCodeGetSN)//是否基于零件码,判断是否过站
@@ -2295,7 +2297,7 @@ namespace TeamAAS_VP.Core
                                         string pressure = string.Join(",", pressureList);
                                         string turn = string.Join(",", turnList);
                                         //这里现在上传的结果都是锁付站的,如果存在其他附属站的数据需要上传时,将不适用,要根据实际情况进行调整
-                                        (bool isSuccess, string response) = await _mesService.SubmitGetAsync(ProductMainSN[i], "", torque, pressure, turn, resultIndex == 1 ? true : false, _LastMesCheckTime[i], DateTime.UtcNow);
+                                        (bool isSuccess, string response) = await _mesService.SubmitGetAsync(ProductMainSN[i], "", torque, pressure, turn, resultIndex == 1 ? true : false, _LastMesCheckTime[i], DateTime.Now);
                                         if (isSuccess)
                                         {
                                             LogHelper.WriteLogMes($"上传mes成功");
@@ -2431,6 +2433,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
                                                 {

+ 2 - 2
TeamAAS-VM/Core/Robots/XYZU_Robot.cs

@@ -548,7 +548,7 @@ namespace TeamAAS_VP.Core.Robots
                 foreach (var node in actNodes)
                 {
                     lastPos[node] = float.NaN;
-                    lastChange[node] = DateTime.UtcNow;
+                    lastChange[node] = DateTime.Now;
                 }
 
                 const float movementThreshold = 0.01f; // 判断位置变化的阈值,避免噪声
@@ -570,7 +570,7 @@ namespace TeamAAS_VP.Core.Robots
                         // 如果读取失败,继续让 CheckMoveFinished 来处理状态或超时
                     }
 
-                    var now = DateTime.UtcNow;
+                    var now = DateTime.Now;
 
                     // 更新每个节点的变化时间:只要任意一个轴在最近 Timeout 时间内有变化,就视为系统仍在运动
                     foreach (var node in actNodes)

+ 26 - 26
TeamAAS-VM/Data/SystemDatabaseService.cs

@@ -128,7 +128,7 @@ namespace TeamAAS_VP.Data
             {
                 UserId = userId,
                 Success = true,
-                Time = DateTime.UtcNow,
+                Time = DateTime.Now,
                 Message = "Set as current user"
             };
 
@@ -148,7 +148,7 @@ namespace TeamAAS_VP.Data
             {
                 UserId = user.Id,
                 Success = true,
-                Time = DateTime.UtcNow,
+                Time = DateTime.Now,
                 Message = user.UserName
             };
             await RecordUserLoginAsync(rec);
@@ -192,7 +192,7 @@ namespace TeamAAS_VP.Data
         public async Task RecordUserLoginAsync(UserLoginRecord record)
         {
             if (record == null) throw new ArgumentNullException(nameof(record));
-            if (record.Time == default(DateTime)) record.Time = DateTime.UtcNow;
+            if (record.Time == default(DateTime)) record.Time = DateTime.Now;
             await _db.Insertable(record).ExecuteCommandAsync();
         }
 
@@ -399,9 +399,9 @@ namespace TeamAAS_VP.Data
         public async Task<int> GetTodayProductionAsync(string productName)
         {
             //今天的起始时间
-            var todayStart = DateTime.UtcNow.Date;
+            var todayStart = DateTime.Now.Date;
             //今天的结束时间
-            var todayEnd = DateTime.UtcNow.Date;
+            var todayEnd = DateTime.Now.Date;
             todayEnd = todayEnd.AddDays(1);
             //按照产品名称和时间范围查询生产记录的总数
             var total = await _db.Queryable<ProductionRecord>()
@@ -417,8 +417,8 @@ namespace TeamAAS_VP.Data
         /// <returns></returns>
         public async Task<int> GetLastHourProductionAsync(string productName)
         {
-            var lastHourStart = DateTime.UtcNow.AddHours(-1);
-            var lastHourEnd = DateTime.UtcNow;
+            var lastHourStart = DateTime.Now.AddHours(-1);
+            var lastHourEnd = DateTime.Now;
             //按照产品名称和时间范围查询生产记录的总数
             var total = await _db.Queryable<ProductionRecord>()
                 .Where(r => r.ProductName == productName && r.Timestamp >= lastHourStart && r.Timestamp < lastHourEnd)
@@ -434,7 +434,7 @@ namespace TeamAAS_VP.Data
         public Task<int> GetCurrentMonthProductionAsync(string productName)
         {
 
-            var now = DateTime.UtcNow;
+            var now = DateTime.Now;
             var monthStart = new DateTime(now.Year, now.Month, 1);
             var monthEnd = monthStart.AddMonths(1);
             //按照产品名称和时间范围查询生产记录的总数
@@ -451,7 +451,7 @@ namespace TeamAAS_VP.Data
         /// <returns></returns>
         public Task<int> GetCurrentWeekProductionAsync(string productName)
         {
-            var now = DateTime.UtcNow;
+            var now = DateTime.Now;
             var weekStart = FirstDayOfWeek(now);
             var weekEnd = weekStart.AddDays(7);
             //按照产品名称和时间范围查询生产记录的总数
@@ -524,7 +524,7 @@ namespace TeamAAS_VP.Data
         /// <returns></returns>
         public async Task<Dictionary<DateTime, Dictionary<string, int>>> GetMonthlyProductionByDayAndCategoryAsync(string productName)
         {
-            var now = DateTime.UtcNow;
+            var now = DateTime.Now;
             var monthStart = new DateTime(now.Year, now.Month, 1);
             var monthEnd = monthStart.AddMonths(1);
             var list = await _db.Queryable<ProductionRecord>()
@@ -549,7 +549,7 @@ namespace TeamAAS_VP.Data
         /// <returns></returns>
         public async Task<Dictionary<int, Dictionary<string, int>>> GetWeeklyProductionByDayAndCategoryAsync(string productName)
         {
-            var now = DateTime.UtcNow;
+            var now = DateTime.Now;
             var weekStart = FirstDayOfWeek(now);
             var weekEnd = weekStart.AddDays(7);
             var list = await _db.Queryable<ProductionRecord>()
@@ -573,7 +573,7 @@ namespace TeamAAS_VP.Data
         /// <returns></returns>
         public async Task<Dictionary<int, Dictionary<string, int>>> GetDailyProductionByHourAndCategoryAsync(string productName)
         {
-            var now = DateTime.UtcNow;
+            var now = DateTime.Now;
             var dayStart = now.Date;
             var dayEnd = dayStart.AddDays(1);
             var list = await _db.Queryable<ProductionRecord>()
@@ -616,7 +616,7 @@ namespace TeamAAS_VP.Data
         public async Task RecordAlarmAsync(AlarmRecord alarm)
         {
             if (alarm == null) throw new ArgumentNullException(nameof(alarm));
-            if (alarm.Time == default(DateTime)) alarm.Time = DateTime.UtcNow;
+            if (alarm.Time == default(DateTime)) alarm.Time = DateTime.Now;
             await _db.Insertable(alarm).ExecuteCommandAsync();
         }
 
@@ -649,7 +649,7 @@ namespace TeamAAS_VP.Data
             try
             {
                 if (result == null) throw new ArgumentNullException(nameof(result));
-                if (result.Timestamp == default(DateTime)) result.Timestamp = DateTime.UtcNow;
+                if (result.Timestamp == default(DateTime)) result.Timestamp = DateTime.Now;
                 await _db.Insertable(result).ExecuteCommandAsync();
             }
             catch (Exception)
@@ -703,7 +703,7 @@ namespace TeamAAS_VP.Data
         public async Task RecordScrewFeederBatchAsync(ScrewFeederBatchRecord record)
         {
             if (record == null) throw new ArgumentNullException(nameof(record));
-            if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.UtcNow;
+            if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.Now;
             record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id;
             await _db.Insertable(record).ExecuteCommandAsync();
         }
@@ -777,7 +777,7 @@ namespace TeamAAS_VP.Data
         public async Task RecordPhotoCaptureAsync(PhotoCaptureRecord record)
         {
             if (record == null) throw new ArgumentNullException(nameof(record));
-            if (record.CaptureTime == default(DateTime)) record.CaptureTime = DateTime.UtcNow;
+            if (record.CaptureTime == default(DateTime)) record.CaptureTime = DateTime.Now;
             record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id;
             await _db.Insertable(record).ExecuteCommandAsync();
         }
@@ -831,7 +831,7 @@ namespace TeamAAS_VP.Data
         public async Task RecordHeadChangeAsync(HeadChangeRecord record)
         {
             if (record == null) throw new ArgumentNullException(nameof(record));
-            if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.UtcNow;
+            if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.Now;
             record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id;
             await _db.Insertable(record).ExecuteCommandAsync();
         }
@@ -895,7 +895,7 @@ namespace TeamAAS_VP.Data
                 {
                     var newRec = new HeadChangeRecord
                     {
-                        ChangeTime = DateTime.UtcNow,
+                        ChangeTime = DateTime.Now,
                         HeadBatchNumber = string.Empty,
                         CurrentLockCount = 1,
                         OperatorName = operatorName
@@ -928,7 +928,7 @@ namespace TeamAAS_VP.Data
         public async Task RecordNozzleChangeAsync(NozzleChangeRecord record)
         {
             if (record == null) throw new ArgumentNullException(nameof(record));
-            if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.UtcNow;
+            if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.Now;
             record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id;
             await _db.Insertable(record).ExecuteCommandAsync();
         }
@@ -975,7 +975,7 @@ namespace TeamAAS_VP.Data
                 {
                     var newRec = new NozzleChangeRecord
                     {
-                        ChangeTime = DateTime.UtcNow,
+                        ChangeTime = DateTime.Now,
                         NozzleBatchNumber = string.Empty,
                         CurrentLockCount = 1,
                         OperatorName = operatorName
@@ -1026,7 +1026,7 @@ namespace TeamAAS_VP.Data
             {
                 var record = new NozzleChangeRecord
                 {
-                    ChangeTime = DateTime.UtcNow.AddMinutes(-i * 10),
+                    ChangeTime = DateTime.Now.AddMinutes(-i * 10),
                     NozzleBatchNumber = $"Batch-{i}",
                     CurrentLockCount = i,
                     OperatorName = $"Operator-{i}"
@@ -1039,7 +1039,7 @@ namespace TeamAAS_VP.Data
             {
                 var record = new HeadChangeRecord
                 {
-                    ChangeTime = DateTime.UtcNow.AddMinutes(-i * 15),
+                    ChangeTime = DateTime.Now.AddMinutes(-i * 15),
                     HeadBatchNumber = $"HeadBatch-{i}",
                     CurrentLockCount = i,
                     OperatorName = $"HeadOperator-{i}"
@@ -1052,7 +1052,7 @@ namespace TeamAAS_VP.Data
             {
                 var record = new ScrewFeederBatchRecord
                 {
-                    ChangeTime = DateTime.UtcNow.AddMinutes(-i * 20),
+                    ChangeTime = DateTime.Now.AddMinutes(-i * 20),
                     BatchNumber = $"ScrewBatch-{i}",
                     FeederNumber = i + 1
                 };
@@ -1068,7 +1068,7 @@ namespace TeamAAS_VP.Data
                     ProductName = "产品1",
                     Category = (i % 2 == 0) ? "OK" : "NG",
                     Quantity = 1,
-                    Timestamp = DateTime.UtcNow.AddMinutes(-i * 5),
+                    Timestamp = DateTime.Now.AddMinutes(-i * 5),
                     UserName = "TestUser",
                     Remark = $"TotalSeconds:1.10"
                 };
@@ -1081,7 +1081,7 @@ namespace TeamAAS_VP.Data
                 var result = new LockResult(i % 4, $"PN-{i % 5}", 10.5f, 0.3f, (i % 3 != 0))
                 {
                     ProductName = "产品1",
-                    Timestamp = DateTime.UtcNow.AddMinutes(-i * 3),
+                    Timestamp = DateTime.Now.AddMinutes(-i * 3),
                 };
                 await RecordLockResultAsync(result);
             }
@@ -1098,7 +1098,7 @@ namespace TeamAAS_VP.Data
                         CameraName = $"下相机",
                         ProductSN = $"SN-{i}",
                         IsSuccess = (i % 2 == 0),
-                        CaptureTime = DateTime.UtcNow.AddMinutes(-i * 7),
+                        CaptureTime = DateTime.Now.AddMinutes(-i * 7),
                         ImagePath = $"/images/photo_{i}.jpg"
                     };
                     await RecordPhotoCaptureAsync(record);

+ 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);
     }
 }

+ 1 - 1
TeamAAS-VM/Models/AlarmRecord.cs

@@ -26,6 +26,6 @@ namespace TeamAAS_VP.Models
         [SugarColumn(Length = 255, IsNullable = true)]
         public string Source { get; set; }
 
-        public DateTime Time { get; set; } = DateTime.UtcNow;
+        public DateTime Time { get; set; } = DateTime.Now;
     }
 }

+ 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; }
+    }
+}

+ 1 - 1
TeamAAS-VM/Models/HeadChangeRecord.cs

@@ -12,7 +12,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// ¸ü»»Ê±¼ä
         /// </summary>
-        public DateTime ChangeTime { get; set; } = DateTime.UtcNow;
+        public DateTime ChangeTime { get; set; } = DateTime.Now;
 
         /// <summary>
         /// ÅûÍ·ÅúºÅ

+ 1 - 1
TeamAAS-VM/Models/NozzleChangeRecord.cs

@@ -12,7 +12,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// ¸ü»»Ê±¼ä
         /// </summary>
-        public DateTime ChangeTime { get; set; } = DateTime.UtcNow;
+        public DateTime ChangeTime { get; set; } = DateTime.Now;
 
         /// <summary>
         /// Îü×ìÅúºÅ

+ 1 - 1
TeamAAS-VM/Models/PhotoCaptureRecord.cs

@@ -12,7 +12,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// ÅÄÕÕʱ¼ä
         /// </summary>
-        public DateTime CaptureTime { get; set; } = DateTime.UtcNow;
+        public DateTime CaptureTime { get; set; } = DateTime.Now;
 
         /// <summary>
         /// Åä·½Ãû

+ 1 - 1
TeamAAS-VM/Models/ProductionRecord.cs

@@ -28,7 +28,7 @@ namespace TeamAAS_VP.Models
         [SugarColumn(Length = 255, IsNullable = true)]
         public string DeviceName { get; set; }
 
-        public DateTime Timestamp { get; set; } = DateTime.UtcNow;
+        public DateTime Timestamp { get; set; } = DateTime.Now;
         /// <summary>
         /// Category like "OK", "NG", etc.
         /// </summary>

+ 1 - 1
TeamAAS-VM/Models/ScrewFeederBatchRecord.cs

@@ -23,7 +23,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// 更换批次的时间
         /// </summary>
-        public DateTime ChangeTime { get; set; } = DateTime.UtcNow;
+        public DateTime ChangeTime { get; set; } = DateTime.Now;
 
         /// <summary>
         /// 操作员姓名

+ 1 - 1
TeamAAS-VM/Models/UserLoginRecord.cs

@@ -11,7 +11,7 @@ namespace TeamAAS_VP.Models
 
         public int UserId { get; set; }
 
-        public DateTime Time { get; set; } = DateTime.UtcNow;
+        public DateTime Time { get; set; } = DateTime.Now;
 
         public bool Success { get; set; }
 

+ 1 - 1
TeamAAS-VM/Services/ConfigService.cs

@@ -1519,7 +1519,7 @@ namespace TeamAAS_VP.Services
                 {
                     BatchNumber = batchNumber,
                     PackSize = packSize,
-                    ChangeTime = DateTime.UtcNow,
+                    ChangeTime = DateTime.Now,
                     RemainingCount = remainingAfter,
                     FeederNumber = feederNumber,
                     FeederName = feederName,

+ 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 - 4
TeamAAS-VM/Services/RemoteCommandService.cs

@@ -714,10 +714,10 @@ namespace TeamAAS_VP.Services
             var token = _cycleTimingCts.Token;
             Task.Run(async () =>
             {
-                var startTime = DateTime.UtcNow;
+                var startTime = DateTime.Now;
                 while (!token.IsCancellationRequested)
                 {
-                    var elapsed = DateTime.UtcNow - startTime;
+                    var elapsed = DateTime.Now - startTime;
                     OnCycleTimingTicked(elapsed);
                     await Task.Delay(100, token);
                 }
@@ -749,7 +749,7 @@ namespace TeamAAS_VP.Services
                 Category = countItem,
                 ProductName = _productService.GetCurrentProduct()?.Name ?? "Unknown",
                 Quantity = 1,
-                Timestamp = DateTime.UtcNow
+                Timestamp = DateTime.Now
             });
             return Task.FromResult(true);
         }
@@ -788,7 +788,7 @@ namespace TeamAAS_VP.Services
                 Code = alarmCode,
                 Message = alarmDescription,
                 Source = "Unknown",
-                Time = DateTime.UtcNow
+                Time = DateTime.Now
             });
             return Task.FromResult(true);
         }

+ 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" />

+ 3 - 3
TeamAAS-VM/ViewModels/Home/AddScrewViewModel.cs

@@ -141,7 +141,7 @@ namespace TeamAAS_VP.ViewModels.Home
                 SelectedFeeder.BatchNumber = refreshed.BatchNumber;
                 CurrentBatchNumber = refreshed.BatchNumber;
                 RemainingCount = refreshed.RemainingCount;
-                LastAddTime = DateTime.UtcNow.ToString("u");
+                LastAddTime = DateTime.Now.ToString("u");
             }
             Management management = _container.Resolve<Management>();
             if (SelectedFeeder.FeederNumber == 1)
@@ -156,7 +156,7 @@ namespace TeamAAS_VP.ViewModels.Home
                 {
                     BatchNumber = NewBatchNumber,
                     PackSize = SelectedFeeder.PackSize,
-                    ChangeTime = DateTime.UtcNow,
+                    ChangeTime = DateTime.Now,
                     RemainingCount = SelectedFeeder.RemainingCount,
                     FeederNumber = SelectedFeeder.FeederNumber,
                     FeederName = SelectedFeeder.Name,
@@ -182,7 +182,7 @@ namespace TeamAAS_VP.ViewModels.Home
                 SelectedFeeder.BatchNumber = refreshed.BatchNumber;
                 CurrentBatchNumber = refreshed.BatchNumber;
                 RemainingCount = refreshed.RemainingCount;
-                LastAddTime = DateTime.UtcNow.ToString("u");
+                LastAddTime = DateTime.Now.ToString("u");
             }
             Management management = _container.Resolve<Management>();
             if (SelectedFeeder.FeederNumber==1)

+ 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));

+ 1 - 1
TeamAAS-VM/ViewModels/User/CardLoginWindowViewModel.cs

@@ -246,7 +246,7 @@ namespace TeamAAS_VP.ViewModels.User
 
                     // mark current user in system DB and record login
                     await _systemDatabaseService.SetCurrentUserAsync(user.Id);
-                    await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.UtcNow, Message = "Login success" });
+                    await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.Now, Message = "Login success" });
                     _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
                     View.DialogResult = true;
                 }

+ 2 - 2
TeamAAS-VM/ViewModels/User/LoginViewModel.cs

@@ -151,7 +151,7 @@ namespace TeamAAS_VP.ViewModels.User
 
                     // mark current user in system DB and record login
                     await _systemDatabaseService.SetCurrentUserAsync(user.Id);
-                    //await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.UtcNow, Message = "Login success" });
+                    //await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.Now, Message = "Login success" });
 
                     _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.登录成功}!", Duration = 1 });
                     _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
@@ -160,7 +160,7 @@ namespace TeamAAS_VP.ViewModels.User
             catch (Exception ex)
             {
                 // authentication failed or error
-                await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = 0, Success = false, Time = DateTime.UtcNow, Message = $"Login failed for {SelectedUserName}: {ex.Message}" });
+                await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = 0, Success = false, Time = DateTime.Now, Message = $"Login failed for {SelectedUserName}: {ex.Message}" });
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.密码错误, Duration = 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)