637 lines
26 KiB
C#
637 lines
26 KiB
C#
/*
|
|
* Copyright (c) 2022 LG Electronics Inc.
|
|
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
|
|
*/
|
|
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.Api.DataType;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.Remoting.Contexts;
|
|
using System.Runtime.Remoting.Messaging;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Web.UI.WebControls;
|
|
using System.Windows;
|
|
|
|
namespace AuroraUIComponent.Sequence
|
|
{
|
|
public class GlassInLogItem
|
|
{
|
|
public int RecipeId{ get; set; }
|
|
public string RecipeName { get; set; }
|
|
public int LayerNum { get; set; }
|
|
public string Desc { get; set; }
|
|
//public bool IsAging { get; set; }
|
|
//public int OrgRecipeIdFromPLC { get; set; }
|
|
//public int OrgLayerNumdFromPLC { get; set; }
|
|
|
|
public GlassInLogItem(int recipeId, string recipeName, int layerNum, string desc/*, bool isAging = false, int orgRecipeIdFromPLC = -1, int orgLayerNumFromPLC = -1*/)
|
|
{
|
|
RecipeId = recipeId;
|
|
RecipeName = recipeName;
|
|
LayerNum = layerNum;
|
|
Desc = desc;
|
|
|
|
//IsAging = isAging;
|
|
//OrgRecipeIdFromPLC = orgRecipeIdFromPLC;
|
|
//OrgLayerNumdFromPLC = orgLayerNumFromPLC;
|
|
}
|
|
}
|
|
|
|
public class CsvLogSequence
|
|
{
|
|
static readonly Logger Log = new Logger(nameof(CsvLogSequence));
|
|
|
|
/// <summary>
|
|
/// Param 1 : ProcLogId, 2: BeforeFile, 3: NewFile
|
|
/// </summary>
|
|
public const string EVENT_NOTIFY_LOG_FILE_CHANGED = "EVENT_NOTIFY_LOG_FILE_CHANGED"; // file open 시 알림
|
|
|
|
/// <summary>
|
|
/// Log Event (헤더 추가) Publish
|
|
/// </summary>
|
|
public const string EVENT_NOTIFY_HEADER_ADDED = "EVENT_NOTIFY_HEADER_ADDED";
|
|
|
|
/// <summary>
|
|
/// Log Event (로그 추가) Publish
|
|
/// </summary>
|
|
public const string EVENT_NOTIFY_LOG_ADDED = "EVENT_NOTIFY_LOG_ADDED";
|
|
|
|
private static object _lock = new object();
|
|
|
|
#region 시퀀스 노드 이름
|
|
const string SEQ_Init = "Init"; // 딱 한번 Sequence 진입 시에만 실행될 Node
|
|
const string SEQ_Wait = "Wait";
|
|
const string SEQ_CheckNewFile = "CheckNewFile";
|
|
const string SEQ_CheckNewLogData = "CheckNewLogData";
|
|
const string SEQ_CheckSettingUpdated = "CheckSettingUpdated";
|
|
#endregion
|
|
|
|
public static SequenceBuilder Builder()
|
|
{
|
|
SequenceBuilder sb = new SequenceBuilder(1000, true); // 무한, 시퀀스 Stop시 최대 대기시간.
|
|
|
|
sb.AddAction(SEQ_Init, SeqInitNode);
|
|
sb.AddAction(SEQ_Wait, SeqWait);
|
|
sb.AddAction(SEQ_CheckNewFile, SeqCheckNewFile);
|
|
sb.AddAction(SEQ_CheckNewLogData, SeqCheckNewLogData);
|
|
sb.AddDecision(SEQ_CheckSettingUpdated, SeqCheckSettingUpdated);
|
|
|
|
sb.Flows(sb.Init > sb[SEQ_Init] > sb[SEQ_Wait] > sb[SEQ_CheckNewFile] > sb[SEQ_CheckNewLogData] > sb[SEQ_CheckSettingUpdated]);
|
|
sb.Flows(sb[SEQ_CheckSettingUpdated] > sb[SEQ_Wait]);
|
|
sb.Flows(sb[SEQ_CheckSettingUpdated] > sb[SEQ_Init]);
|
|
|
|
sb.OnStopped(OnStopped);
|
|
sb.OnForceError(OnForceError);
|
|
|
|
//sb.Draw(nameof(CsvLogSequence) + ".pdf");
|
|
|
|
return sb;
|
|
}
|
|
|
|
private static string SeqCheckSettingUpdated(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
|
|
{
|
|
if (NeedUpdate) {
|
|
return SEQ_Init;
|
|
}
|
|
|
|
return SEQ_Wait;
|
|
}
|
|
|
|
private static bool NeedUpdate = false;
|
|
private static string SettingId = null;
|
|
public static void UpdateSetting(string settingId)
|
|
{
|
|
NeedUpdate = true;
|
|
SettingId = settingId;
|
|
}
|
|
|
|
public class CsvLogItemEx : CsvLogSetting.LogItem
|
|
{
|
|
public DateTime CurLogFileTime { get; internal set; }
|
|
public string CurLogFileName { get; internal set; }
|
|
public string CurLogFileFullPath { get; internal set; }
|
|
public string CurLogDir { get; set; }
|
|
|
|
public int CurLogFileIndex { get; internal set; }
|
|
|
|
public Queue<ValueTuple<DateTime, bool, object>> ItemToSave = new Queue<ValueTuple<DateTime, bool, object>>();
|
|
|
|
public FileStream FStream { get; internal set; }
|
|
public StreamWriter FStreamWriter { get; internal set; }
|
|
|
|
public bool IsHeaderWritten { get; internal set; } = false;
|
|
public int LogCount { get; set; } = 0;
|
|
public DateTime TimeResetCheckStartTime;
|
|
|
|
private string CreateFileName(DateTime dt, string prefix, string postfix, int fileNumber)
|
|
{
|
|
return (string.IsNullOrEmpty(prefix) ? "" : prefix + "_")
|
|
+ dt.ToString("yyMMdd-HHmmss")
|
|
+ (string.IsNullOrEmpty(postfix) ? "" : "_" + postfix) + (fileNumber != 0 ? ("." + fileNumber) : string.Empty) + ".csv";
|
|
}
|
|
|
|
internal string CheckAndUpdateFileName(bool initialCheck = false)
|
|
{
|
|
this.CurLogFileTime = DateTime.Now;
|
|
|
|
bool makeNewName = true;
|
|
string _LogFileFullPath;
|
|
|
|
// ReUseFileIfExist : 계속 appen해서 저장해야 한다는 요구사항(AlignResult)을 위한 코드
|
|
var lastFileName = Directory.GetFiles(CurLogDir).OrderBy(file => file).LastOrDefault();
|
|
|
|
if (initialCheck && ReUseFileIfExist && !string.IsNullOrEmpty(lastFileName)) {
|
|
var lastLogFileFullPath = Path.Combine(CurLogDir, lastFileName);
|
|
|
|
if (File.Exists(lastLogFileFullPath)) {
|
|
int logCount = CalculateLineCount(lastFileName);
|
|
if (logCount < MaxLogCount) {
|
|
this.CurLogFileName = lastFileName;
|
|
makeNewName = false;
|
|
|
|
Log.W($"ReUse Exist File :: Log[{this.Name}] File[{CurLogFileName}] :: Start from Line[{logCount}]");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (makeNewName) {
|
|
var newFileTime = DateTime.Now;
|
|
if (newFileTime.ToString("yyMMdd-HHmmss") != this.CurLogFileTime.ToString("yyMMdd-HHmmss")) {
|
|
this.CurLogFileIndex = 0;
|
|
}
|
|
this.CurLogFileName = CreateFileName(this.CurLogFileTime, this.Prefix, this.Postfix, this.CurLogFileIndex);
|
|
|
|
// 이미 해당 파일이 있을 때를 위한 Index++
|
|
_LogFileFullPath = Path.Combine(CurLogDir, this.CurLogFileName);
|
|
if (File.Exists(_LogFileFullPath)) {
|
|
while (File.Exists(_LogFileFullPath)) {
|
|
this.CurLogFileIndex++;
|
|
|
|
if (CurLogFileIndex > 100) {
|
|
// 일단 개발 중 방어코드.
|
|
return string.Empty;
|
|
}
|
|
|
|
this.CurLogFileName = CreateFileName(this.CurLogFileTime, this.Prefix, this.Postfix, this.CurLogFileIndex);
|
|
_LogFileFullPath = Path.Combine(CurLogDir, this.CurLogFileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
_LogFileFullPath = Path.Combine(CurLogDir, this.CurLogFileName);
|
|
|
|
CurLogFileFullPath = _LogFileFullPath;
|
|
|
|
return CurLogFileFullPath;
|
|
}
|
|
|
|
internal int CalculateLineCount(string filePath)
|
|
{
|
|
int lineCount = 0;
|
|
if (File.Exists(filePath)) {
|
|
using (var reader = new StreamReader(filePath)) {
|
|
while (reader.ReadLine() != null) {
|
|
lineCount++;
|
|
}
|
|
}
|
|
}
|
|
return lineCount;
|
|
}
|
|
|
|
internal void MakeFileStream()
|
|
{
|
|
LogCount = CalculateLineCount(this.CurLogFileFullPath);
|
|
|
|
if (LogCount > 0) {
|
|
Log.I($"Log[{this.Name}] File[{CurLogFileFullPath}] :: Start from Line[{LogCount}]");
|
|
LogCount--; // Header까지 한줄 추가되어 있는 상태이므로 빼기
|
|
}
|
|
|
|
this.FStream = new FileStream(this.CurLogFileFullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
|
|
this.FStream.Seek(0, SeekOrigin.End); // Move the stream position to the end of the file
|
|
this.FStreamWriter = new StreamWriter(this.FStream);
|
|
this.FStreamWriter.AutoFlush = true;
|
|
|
|
IsHeaderWritten = (LogCount > 0);
|
|
|
|
TimeResetCheckStartTime = DateTime.Now;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 헤더 추가
|
|
/// </summary>
|
|
/// <param name="obj">DateTime 포함된 ValueTuple</param>
|
|
public string InsertHeader(ValueTuple<DateTime, bool, object> obj)
|
|
{
|
|
if (this.FStream == null) return null;
|
|
if (this.FStreamWriter == null) return null;
|
|
|
|
string headerLine = null;
|
|
if (obj.Item3 != null) {
|
|
FStreamWriter.Write("DateTime,");
|
|
headerLine = "DateTime,";
|
|
foreach (var prop in obj.Item3.GetType().GetProperties()) {
|
|
FStreamWriter.Write(prop.Name);
|
|
FStreamWriter.Write(",");
|
|
headerLine = headerLine + prop.Name + ",";
|
|
}
|
|
FStreamWriter.WriteLine("");
|
|
IsHeaderWritten = true;
|
|
}
|
|
|
|
// 헤더를 넣을 때 (= 첫 데이터를 넣을 때) 시간 리셋
|
|
TimeResetCheckStartTime = DateTime.Now;
|
|
return headerLine;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 로그 내용 한 줄 Write
|
|
/// </summary>
|
|
/// <param name="obj">LogItem (DateTime 포함된 ValueTuple 아님)</param>
|
|
/// <param name="dateTime">로그 Timestamp</param>
|
|
internal string WriteLog(object obj, DateTime dateTime)
|
|
{
|
|
if (this.FStream == null) return null;
|
|
if (this.FStreamWriter == null) return null;
|
|
|
|
string headerLine = null;
|
|
if (obj != null) {
|
|
FStreamWriter.Write(dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff,"));
|
|
headerLine = dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff,");
|
|
foreach (var prop in obj.GetType().GetProperties()) {
|
|
FStreamWriter.Write(prop.GetValue(obj));
|
|
FStreamWriter.Write(",");
|
|
headerLine = headerLine + prop.GetValue(obj) + ",";
|
|
}
|
|
FStreamWriter.WriteLine("");
|
|
|
|
LogCount++;
|
|
|
|
return headerLine;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal void CloseFileStream()
|
|
{
|
|
if (this.FStreamWriter != null) {
|
|
this.FStreamWriter.Close();
|
|
}
|
|
if (this.FStream != null) {
|
|
this.FStream.Close();
|
|
}
|
|
|
|
//if (!string.IsNullOrEmpty(this.CurLogFileName) && !string.IsNullOrEmpty(this.CurLogFileFullPath) && File.Exists(this.CurLogFileFullPath)) {
|
|
// if (File.ReadAllText(this.CurLogFileFullPath).Length <= 0) {
|
|
// // Empty File 삭제
|
|
// File.Delete(this.CurLogFileFullPath);
|
|
// }
|
|
//}
|
|
|
|
if (!this.Enable) return;
|
|
|
|
if (this.CurLogFileIndex >= 0) {
|
|
this.CurLogFileIndex++;
|
|
}
|
|
}
|
|
|
|
internal long GetFileSizeInByte()
|
|
{
|
|
if (FStream != null) {
|
|
return new FileInfo(FStream.Name).Length; // Byte 단위
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
internal long GetFileSizeInMB()
|
|
{
|
|
if (FStream != null) {
|
|
return new FileInfo(FStream.Name).Length / 1024 / 1024; // MB 단위
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
internal bool CheckDir()
|
|
{
|
|
string _LogDir;
|
|
|
|
// DataPath (Recipe 경로) 아래에 넣지 않을 수도 있도록 절대경로 확인
|
|
if (this.LogDir.Contains(":\\")) {
|
|
var driveName = this.LogDir.Split(':').FirstOrDefault();
|
|
if (!string.IsNullOrWhiteSpace(driveName) && DriveInfo.GetDrives().Any(drive => drive.IsReady && drive.Name == (driveName + ":\\"))) {
|
|
// 정상 드라이브. Full Path 그대로 사용
|
|
_LogDir = this.LogDir;
|
|
} else {
|
|
// 비정상 케이스
|
|
Log.FuncE("Invalid Directory : " + this.LogDir);
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!string.IsNullOrEmpty(ApplicationExt.DataPath)) {
|
|
_LogDir = Path.Combine(ApplicationExt.DataPath, this.LogDir);
|
|
} else {
|
|
_LogDir = this.LogDir; // out 폴더 밑에
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(_LogDir)) {
|
|
return false;
|
|
}
|
|
|
|
if (!Directory.Exists(_LogDir)) {
|
|
Directory.CreateDirectory(_LogDir);
|
|
}
|
|
|
|
if (!Directory.Exists(_LogDir)) {
|
|
Log.E("Something Wrong :: Cannot Create Directory");
|
|
this.Enable = false;
|
|
}
|
|
if (!this.Enable) return false;
|
|
|
|
this.CurLogDir = _LogDir;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private static List<CsvLogItemEx> Logs = new List<CsvLogItemEx>();
|
|
private static List<string> OpenFileList = new List<string>();
|
|
|
|
private static SequenceActionResult SeqInitNode(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
NeedUpdate = false;
|
|
|
|
Log.FuncI("enter");
|
|
|
|
if (string.IsNullOrEmpty(SettingId)) return SequenceAction.Ok();
|
|
|
|
var setting = context.Settings.Get(SettingId) as CsvLogSetting;
|
|
if (setting == null) return SequenceAction.Ok();
|
|
|
|
lock (_lock) {
|
|
// CsvLogSetting이 바뀌어서 Sequence를 초기화할 때 : 기존에 열려있던 데이터 close
|
|
foreach (var logItem in Logs) {
|
|
logItem.CloseFileStream();
|
|
}
|
|
|
|
// 기본 세팅 복사
|
|
foreach (var logSetting in setting.Items) {
|
|
if (!Logs.Any(x => x.Name == logSetting.Name)) {
|
|
Logs.Add(new CsvLogItemEx() { Name = logSetting.Name });
|
|
}
|
|
|
|
var logItem = Logs.FirstOrDefault(x => x.Name == logSetting.Name);
|
|
logItem.Copy(logSetting);
|
|
}
|
|
// 이미 Add되어 있던 LogItem(CsvLogItemEx)이 없어졌더라도 삭제하지 않고 종료시까지 유지
|
|
|
|
// 중복체크
|
|
for (int i = 0; i < Logs.Count(); i++) {
|
|
if (Logs.Take(i).Any(x => x.LogDir == Logs[i].LogDir)) {
|
|
// Dir이 같게 설정된 폴더가 있음 : Disable
|
|
Log.E($"Duplicated LogDir :: Check [{Logs[i].Name}] Setting !!");
|
|
Logs[i].Enable = false;
|
|
}
|
|
}
|
|
|
|
foreach (var logItem in Logs) {
|
|
if (!logItem.Enable) continue;
|
|
|
|
if (string.IsNullOrEmpty(logItem.LogDir)) {
|
|
logItem.Enable = false;
|
|
}
|
|
if (!logItem.Enable) continue;
|
|
|
|
// 폴더 체크 및 생성
|
|
if (logItem.CheckDir() == false) {
|
|
Log.E($"Invalid LogDir :: Check #[{logItem.Name}] Setting !!");
|
|
continue;
|
|
}
|
|
|
|
// 로그파일 이름 생성 (이미 존재할 경우 INDEX ++, ReUse option 켜져있으면 이전 파일 다시 사용)
|
|
logItem.CheckAndUpdateFileName(initialCheck: true);
|
|
|
|
if (string.IsNullOrEmpty(logItem.CurLogFileFullPath)) {
|
|
Log.E("Something Wrong :: Cannot Create Full Path");
|
|
logItem.Enable = false;
|
|
}
|
|
if (!logItem.Enable) continue;
|
|
|
|
// 파일스트림 OPEN
|
|
logItem.MakeFileStream();
|
|
|
|
OpenFileList.Add(logItem.CurLogFileFullPath);
|
|
|
|
Log.D("Make LogFile : " + logItem.CurLogFileFullPath);
|
|
|
|
context.EventPublisher.Publish(new EventMessage("CsvLogSeq", EVENT_NOTIFY_LOG_FILE_CHANGED, (logItem.Name, string.Empty, logItem.CurLogFileName)));
|
|
}
|
|
}
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
|
|
private static SequenceActionResult SeqWait(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
// 몇초마다 flush할 지 조정
|
|
Thread.Sleep(1000);
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// CEqpSeq::InterlockProcess() 포팅
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <param name="id"></param>
|
|
/// <param name="param"></param>
|
|
/// <returns></returns>
|
|
private static SequenceActionResult SeqCheckNewFile(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
Thread.Sleep(10);
|
|
|
|
lock (_lock) {
|
|
foreach (var logItem in Logs) {
|
|
if (!logItem.Enable) continue;
|
|
|
|
if (File.Exists(logItem.CurLogFileFullPath)) {
|
|
bool needToChange = false;
|
|
|
|
// 새 파일로 변경 필요한 경우
|
|
if (logItem.MaxFileSizeInMB > 0 && logItem.GetFileSizeInMB() >= logItem.MaxFileSizeInMB) {
|
|
Log.FuncV($"Make New File Because of MaxFileSizeInMB : {logItem.GetFileSizeInMB()} >= {logItem.MaxFileSizeInMB}");
|
|
needToChange = true;
|
|
} else if (logItem.MaxLogCount > 0 && logItem.LogCount >= logItem.MaxLogCount) {
|
|
Log.FuncV($"Make New File Because of MaxLogCount : {logItem.LogCount} >= {logItem.MaxLogCount})");
|
|
needToChange = true;
|
|
} else if (logItem.MaxTimeIntervalInMinute > 0) {
|
|
var curTime = DateTime.Now;
|
|
TimeSpan timeDifference = curTime - logItem.TimeResetCheckStartTime;
|
|
if (timeDifference.TotalSeconds / 60 >= logItem.MaxTimeIntervalInMinute) {
|
|
if (logItem.LogCount > 0) {
|
|
// 해당 시간 동안 쌓인 데이터가 있었다면 로그 파일 change (+ Publish Event)
|
|
needToChange = true;
|
|
Log.FuncV($"Make New File Because of New Log[{logItem.LogCount}] While : {timeDifference.TotalSeconds / 60} >= {logItem.MaxTimeIntervalInMinute})");
|
|
} else {
|
|
// 이 시점까지 로그가 없었다면 타이머를 Reset
|
|
logItem.TimeResetCheckStartTime = curTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (needToChange) {
|
|
// 파일스트림 CLOSE
|
|
logItem.CloseFileStream();
|
|
string lastFileName = logItem.CurLogFileName;
|
|
|
|
// 로그파일 이름 생성 (이미 존재할 경우 INDEX ++)
|
|
logItem.CheckAndUpdateFileName();
|
|
|
|
if (string.IsNullOrEmpty(logItem.CurLogFileFullPath)) {
|
|
Log.E("Something Wrong :: Cannot Create Full Path");
|
|
logItem.Enable = false;
|
|
}
|
|
if (!logItem.Enable) continue;
|
|
|
|
// 파일스트림 OPEN
|
|
logItem.MakeFileStream();
|
|
OpenFileList.Add(logItem.CurLogFileFullPath);
|
|
|
|
string curFileName = logItem.CurLogFileName;
|
|
|
|
Log.W("Make LogFile : " + logItem.CurLogFileFullPath);
|
|
|
|
context.EventPublisher.Publish(new EventMessage("CsvLogSeq", EVENT_NOTIFY_LOG_FILE_CHANGED, (logItem.Name, lastFileName, curFileName)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
|
|
private static Stopwatch _sw = new Stopwatch();
|
|
private static SequenceActionResult SeqCheckNewLogData(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
Thread.Sleep(10);
|
|
|
|
// 마그넷조립기 성능개선 : Main Sequence 구동 중에는 CSV Log Write 미루기 => Main Sequence 구동 시나리오에 따라 다르므로 주석처리
|
|
//if (context.Sequences.GetSequences().Where(x => x.SequenceId.Contains("Main")).Any(x => x.RunState == SequenceRunState.Running)) {
|
|
// return SequenceAction.Ok();
|
|
//}
|
|
|
|
lock (_lock) {
|
|
foreach (var logItem in Logs) {
|
|
|
|
if (!logItem.Enable) continue;
|
|
if (logItem.ItemToSave == null || logItem.ItemToSave.Count == 0) continue;
|
|
|
|
Log.FuncI($"[{logItem.Name}] Write Log [{logItem.ItemToSave.Count}]");
|
|
|
|
string logLine;
|
|
// 첫번째 write : header 생성
|
|
//if (logItem.GetFileSizeInByte() <= 0) {
|
|
if (!logItem.IsHeaderWritten) {
|
|
Log.FuncI($"[{logItem.Name}] Insert Header");
|
|
logLine = logItem.InsertHeader(logItem.ItemToSave.FirstOrDefault());
|
|
context.EventPublisher.Publish(new EventMessage("CsvLogSeq", EVENT_NOTIFY_HEADER_ADDED, (logItem.Name, logItem.CurLogFileName, logLine)));
|
|
}
|
|
|
|
while (logItem.ItemToSave.Count() > 0) {
|
|
var logObj = logItem.ItemToSave.Dequeue();
|
|
if (logObj is ValueTuple<DateTime, bool, object> tuple) {
|
|
if (tuple.Item2) {
|
|
logLine = logItem.InsertHeader(tuple);
|
|
context.EventPublisher.Publish(new EventMessage("CsvLogSeq", EVENT_NOTIFY_HEADER_ADDED, (logItem.Name, logItem.CurLogFileName, logLine)));
|
|
}
|
|
logLine = logItem.WriteLog(tuple.Item3, tuple.Item1);
|
|
context.EventPublisher.Publish(new EventMessage("CsvLogSeq", EVENT_NOTIFY_LOG_ADDED, (logItem.Name, logItem.CurLogFileName, logLine)));
|
|
} else {
|
|
logLine = logItem.WriteLog(logObj, DateTime.Now);
|
|
context.EventPublisher.Publish(new EventMessage("CsvLogSeq", EVENT_NOTIFY_LOG_ADDED, (logItem.Name, logItem.CurLogFileName, logLine)));
|
|
}
|
|
}
|
|
|
|
//Log.V($"Log[{logItem.Name}] Size (MB) = {logItem.GetFileSizeInMB()}");
|
|
//Log.V($"Log[{logItem.Name}] Size (KB) = {logItem.GetFileSizeInByte() / 1024}");
|
|
//Log.V($"Log[{logItem.Name}] Size (Byte) = {logItem.GetFileSizeInByte()}");
|
|
}
|
|
}
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log Write할 데이터 object 넣기
|
|
/// Log Processing 코드(SeqCheckNewLogData)와 conflict 발생하지 않도록 async 주의
|
|
/// </summary>
|
|
/// <param name="logName">저장할 CsvLogSetting 로그 이름 (CsvLogIds.xxx)</param>
|
|
/// <param name="obj">저장할 데이터 객체 (주의: 헤더와 불일치하지 않게, 같은 Class Type의 Object만 넣어주세요. 그리고 csv를 만들기 위해 property 1단계만 사용해주세요)</param>
|
|
public static void EnqueueLog(string logName, object obj, bool writeHeader = false)
|
|
{
|
|
if (obj == null) return;
|
|
|
|
lock (_lock) {
|
|
var logItem = Logs.FirstOrDefault(x => x.Name == logName);
|
|
if (logItem != null) {
|
|
Application.Current.Dispatcher.Invoke(delegate {
|
|
logItem.ItemToSave.Enqueue(new ValueTuple<DateTime, bool, object>(DateTime.Now, writeHeader, obj));
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 해당 로그아이템의 Full File Path 획득
|
|
/// </summary>
|
|
/// <param name="logName">CsvLogSetting 로그 이름 (CsvLogIds.xxx)</param>
|
|
/// <param name="fileName">로그파일 이름</param>
|
|
/// <returns></returns>
|
|
public static string GetFullFilePath(string logName, string fileName)
|
|
{
|
|
if (string.IsNullOrEmpty(logName)) return string.Empty;
|
|
|
|
string result = string.Empty;
|
|
|
|
lock (_lock) {
|
|
var logItem = Logs.FirstOrDefault(x => x.Name == logName);
|
|
if (logItem != null) {
|
|
result = Path.Combine(logItem.CurLogDir, fileName);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static void OnForceError(SequenceContext arg1, string arg2)
|
|
{
|
|
|
|
}
|
|
|
|
private static void OnStopped(SequenceContext obj)
|
|
{
|
|
//lock (_lock) {
|
|
try {
|
|
foreach (var logItem in Logs) {
|
|
|
|
// 파일스트림 Close
|
|
logItem.CloseFileStream();
|
|
}
|
|
} catch (Exception ex) {
|
|
Log.FuncE("Close Exception : " + ex.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|