原先打算再寫點EXT.NET方面的,由於現在在玩Silverlight和Android,所以不打算寫下去了。而且最近很忙,所以發帖也不會很及時。
好了,轉入正題了:最近客戶有個需求,要求寫個小程式監控生產線上的蘋果一體機的記錄檔並上傳到伺服器。開始想使用Perl或者Python,但是時間不夠(因為對這兩門語言還不太熟悉),於是想試試MONO。MONO雖然沒想象中好用,但是還算勉強能用。
雖然MonoDevelop還可以,但是還是比較喜歡使用VS開發。於是在VS裡面先編碼。值得注意的是:
- 如果是開發Winform程式,介面用GTK重畫吧。
- 如果是在VS裡面開發,最好將所有的編碼和注釋都用英文。否則換其他系統時,可能會出現亂碼。
- 編寫時需注意MONO是否支援,否則就是白寫。
接下來,開始編碼。
1.讀取設定檔
習慣用XML,發現不支援System.Xml.Linq,亦不支援System.Xml,讀取ini檔案也麻煩,於是乾脆讀取文字檔好了。樣本配置如下:
MonitoringDirectoryType:['Immediate','Cycle']Path:['/Users/PRODUCTION/Public/FinalTest/LogsFolder','/Users/PRODUCTION/Public/FinalTest/LogsFolder']TargetPath:['/Volumes/mes_data/FINALTEST/n81a/{Y}/{M}/{D}/FIN012','/Volumes/mes_data/FINALTEST/n81a/{Y}/{M}/{D}/FIN012']IncludeSubdirectories:['FALSE','false']Filter:['*.TXT','*.CSV']BackupPath:['/Users/PRODUCTION/BACKUP/{Y}/{M}/{D}','/Users/PRODUCTION/BACKUP/{Y}/{M}/{D}']BackupExpired:['2','2']CycleMinutes:['','1440']
相關解析代碼如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using MonitoringApp.Custom;using System.Text.RegularExpressions;using Gtk;namespace MonitoringApp.Code.App{ public class Configuration { private static bool isLoadFirst = false; private static List<MyConfig> configLst; public static List<MyConfig> ConfigLst { get { if (configLst == null) { try { if (!isLoadFirst) return LoadFromInitParams(); else return null; } catch (Exception ex) { LogManager.WriteErrorLog(ex); return null; } } return configLst; } set { configLst = value; } } /// <summary> /// Load Configuration file /// </summary> /// <returns></returns> private static List<MyConfig> LoadFromInitParams() { isLoadFirst = true; string _configPath = Path.Combine(Directory.GetCurrentDirectory(), "Configuration.txt"); if (File.Exists(_configPath)) { if (ConfigLst == null) ConfigLst = new List<MyConfig>(); else return ConfigLst; using (StreamReader sr = new StreamReader(_configPath)) { int lineIndex = 1; while (sr.Peek() > 0) { string str = sr.ReadLine().Trim(); if (str.Length == 0) continue; if (!str.Contains("[") || !str.Contains("]")) { LogManager.WriteErrorLog(string.Format("Config Error:'[' OR ']' Not Exist.At Line {0}.", lineIndex)); continue; } if (!str.Contains(":")) { LogManager.WriteErrorLog(string.Format("Config Error:':' Not Exist.At Line {0}.", lineIndex)); continue; } string[] names = str.Split(':'); str = str.Substring(str.IndexOf(':') + 1).Trim(']').Trim('[').Trim('\''); string[] strConfigs = str.Split(','); SetConfig(names, ConfigLst, strConfigs, lineIndex == 1); lineIndex++; } } return ConfigLst; } else { LogManager.WriteErrorLog(string.Format("can't find config file.Path:{0}.",_configPath)); return null; } } /// <summary> /// Set Config /// </summary> /// <param name="name"></param> /// <param name="lst"></param> /// <param name="strConfigs"></param> private static void SetConfig(string[] name, List<MyConfig> lst, string[] strConfigs, bool isLineOne) { try { var mcConfig = (MonitoringConfigurations)Enum.Parse(typeof(MonitoringConfigurations), name[0], true); //Set Values for (int i = 0; i < strConfigs.Length; i++) { string value = strConfigs[i].Trim('\''); switch (mcConfig) { case MonitoringConfigurations.MonitoringDirectoryType: { #region MonitoringDirectoryType Defalut:immediate switch (value.ToLower()) { case "immediate": { if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.MDType = MonitoringDirectoryType.Immediate; lst.Add(myConfig); } else { lst[i].MDType = MonitoringDirectoryType.Immediate; } break; } case "cycle": { if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.MDType = MonitoringDirectoryType.Cycle; lst.Add(myConfig); } else { lst[i].MDType = MonitoringDirectoryType.Cycle; } break; } default: { if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.MDType = MonitoringDirectoryType.Immediate; lst.Add(myConfig); } else { lst[i].MDType = MonitoringDirectoryType.Immediate; } break; } } #endregion break; } case MonitoringConfigurations.Path: #region Path if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.Path = value; lst.Add(myConfig); } else { lst[i].Path = value; } #endregion break; case MonitoringConfigurations.IncludeSubdirectories: #region IncludeSubdirectories if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.IncludeSubdirectories = Convert.ToBoolean(value); lst.Add(myConfig); } else { lst[i].IncludeSubdirectories = Convert.ToBoolean(value); } #endregion break; case MonitoringConfigurations.Filter: #region Filter if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.Filter = value; lst.Add(myConfig); } else { lst[i].Filter = value; } break; #endregion case MonitoringConfigurations.BackupPath: #region BackupPath if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.BackupPath = value; lst.Add(myConfig); } else { lst[i].BackupPath = value; } #endregion break; case MonitoringConfigurations.TargetPath: #region TargetPath if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.TargetPath = value; lst.Add(myConfig); } else { lst[i].TargetPath = value; } #endregion break; case MonitoringConfigurations.BackupExpired: #region BackupExpired Unit:days Default:30 if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.BackupExpired = Convert.ToInt32(value.Length == 0 ? "30" : value); lst.Add(myConfig); } else { lst[i].BackupExpired = Convert.ToInt32(value.Length == 0 ? "30" : value); } #endregion break; case MonitoringConfigurations.CycleMinutes: #region CycleMinutes Unit:Minute Default:60 if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.CycleMinutes = Convert.ToInt32(value.Length == 0 ? "60" : value); lst.Add(myConfig); } else { lst[i].CycleMinutes = Convert.ToInt32(value.Length == 0 ? "60" : value); } #endregion break; default: break; } } } catch (Exception ex) { LogManager.WriteErrorLog(ex); } } }}
值得注意的是:
- 這裡是按行讀取的。從讀取配置來看,可以看出,是支援多個配置項的。
- 如果出錯,會記錄日誌。
- 和客戶講解如何配置是一件麻煩的事情。相關配置解釋如下:
MonitoringDirectoryType:目錄類型,僅支援兩個值(Immediate、Cycle)。Immediate表示即時監控(預設值),Cycle表示周期監控。Path:監控目錄路徑。必須是存在的路徑。TargetPath:目標目錄路徑。可以是遠程目錄路徑。不能使用SMB路徑,而應該是用如“/Volumes/mes_data/n81a”的類型。IncludeSubdirectories:是否涵蓋子目錄。Filter:過濾字串。如“*”表示監控所有檔案,“*.txt”表示監控所有的文字檔。BackupPath:備份路徑。BackupExpired:備份到期時間。單位是天。必須為整數。預設值30天。CycleMinutes:周期迴圈時間。單位是分。 必須為整數。預設值60分鐘。
然後針對配置寫個說明文檔,這樣客戶就會配置了。
2)記錄日誌
程式運行起來總不可避免的會遇到各種問題,記錄日誌就不可避免了。相關代碼如下:
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using Gtk;namespace MonitoringApp.Code.App{ public class LogManager { public static object obj = new object(); static LogManager() { } #region Fields and Properties private static string _logPath = string.Empty; public static string LogPath { get { lock (obj) { if (_logPath == string.Empty) { var _logStr = string.Format("{0}/log/{1}/{2}", Path.Combine(Directory.GetCurrentDirectory(), "Data"), DateTime.Now.ToString("yyyy-MM"), DateTime.Now.Day); if (!System.IO.Directory.Exists(_logStr)) System.IO.Directory.CreateDirectory(_logStr); return string.Format("{0}/", _logStr); } return _logPath; } } set { _logPath = value; } } private static string _logFielPrefix = string.Empty; public static string LogFielPrefix { get { return _logFielPrefix; } set { _logFielPrefix = value; } } #endregion #region Log public static void WriteLog(string logFile, string msg) { try { lock (obj) { var _sb = new System.Text.StringBuilder(); _sb.Append(LogPath).Append(LogFielPrefix).Append(logFile).Append(".Log"); using (var _sw = System.IO.File.AppendText(_sb.ToString())) { _sw.WriteLine("==============================={0}===============================", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); _sw.WriteLine(msg); _sw.Flush(); _sw.Close(); Console.WriteLine(msg); } } } catch { } } public static void WriteLog(LogFile logFile, string msg) { WriteLog(logFile.ToString(), msg); } public static void WriteTraceLog(string msg) { Console.WriteLine(msg); } public static void WriteErrorLog(string msg) { WriteLog(LogFile.Error, msg); } public static void WriteErrorLog(Exception ex) { var _sbError = new StringBuilder(); _sbError.Append("[Message]").Append(ex.Message); _sbError.Append("\r\n[TargetSite]").Append(ex.TargetSite); _sbError.Append("\r\n[StackTrace]").Append(ex.StackTrace); _sbError.Append("\r\n[Source]").Append(ex.Source); foreach (var _item in ex.Data.Keys) { _sbError.Append("\r\n[").Append(_item).Append("]").Append(ex.Data[_item]); } if (ex.InnerException != null && ex.InnerException.Message != ex.Message) { _sbError.AppendLine("____________________________________________________________________________________"); _sbError.Append("[Message]").Append(ex.InnerException.Message); _sbError.Append("\r\n[TargetSite]").Append(ex.InnerException.TargetSite); _sbError.Append("\r\n[StackTrace]").Append(ex.InnerException.StackTrace); _sbError.Append("\r\n[Source]").Append(ex.InnerException.Source); _sbError.AppendLine("____________________________________________________________________________________"); } WriteLog(LogFile.Error, _sbError.ToString()); } public static void WriteWarningLog(string msg) { WriteLog(LogFile.Warning, msg); } #endregion } public enum LogFile { Trace, Warning, Error, SQL, System }}
值得注意的是“Directory.GetCurrentDirectory()”可以擷取到目前使用者的個人目錄,至於擷取到程式目前的目錄路徑,Winform的那一套不管用(如果哪位朋友找到了適合的方法,請留言)。蘋果系統和Window系統差別太大,從沒玩過蘋果系統,剛開始玩還挺不習慣的。
接下來,講述如何監控目錄以及定時備份,最後講述如何發布部署。發布部署才是最重要的一環。不上生產線,就不知道會有什麼問題。當初以為這個小程式,一天就能搞定,結果中秋3天都廢了。還好萬幸搞定。時間關係,就此打住。