ringbuffer and statistics

This commit is contained in:
meelstorm
2025-07-18 10:13:46 +02:00
parent d3cb790bd9
commit 40d74d6da6
41 changed files with 1188 additions and 180 deletions

View File

@@ -0,0 +1,14 @@
using Ninject.Extensions.ChildKernel;
using VisionBuilder.UI.Common;
namespace VisionBuilder.UI.Statistics;
public static class ModuleExtensions
{
public static IChildKernel UseStatistics(this IChildKernel self, string cameraName)
{
self.Bind<VisionBuilderStatisticsSettings, ISettings>().ToConstant(new VisionBuilderStatisticsSettings(cameraName));
self.RegisterModule<VisionBuilderStatistics>();
return self;
}
}

View File

@@ -13,9 +13,9 @@ using VisionBuilder.UI.Common.RecipeProcessing;
namespace VisionBuilder.UI.Statistics
{
public class VisionBuilderStatistics:IInitializable
public class VisionBuilderStatistics:IVisionBuilderModule,IDisposable
{
private CsvStatistics _csvStatistics;
private CsvStatistics? _csvStatistics;
private readonly VisionBuilderStatisticsSettings _settings;
private string _filePath;
private readonly IRecognitionControl _recognitionControl;
@@ -29,7 +29,7 @@ namespace VisionBuilder.UI.Statistics
private const string MONTHLY_FILE_NAME = "statistics_month.csv";
private const string EMPTY_DATE = "";
private const string EMPTY_TIME = "";
public bool DoNotDeleteTempFiles { get; set; } = false;
public VisionBuilderStatistics(VisionBuilderStatisticsSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
{
@@ -37,18 +37,20 @@ namespace VisionBuilder.UI.Statistics
_recognitionControl = recognitionControl;
_loadingService = loadingService;
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
_recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
}
private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
{
var endedAt = obj.SessionEnded;
_endTime = endedAt;
_loadingService.StartLoading("Writing statistics for " + _settings.CameraName);
try
{
_csvStatistics.WriteLine([_startTime.ToString("d"), _startTime.ToString("T"), DateTime.Now.ToString("d"), DateTime.Now.ToString("T"), "", "", ""]);
_csvStatistics.WriteLine([_startTime.ToString("d"), _startTime.ToString("T"), endedAt.ToString("d"), endedAt.ToString("T"), "", "", ""]);
_csvStatistics.Dispose();
_csvStatistics = null;
WriteFinalStatistics();
}
finally
@@ -67,10 +69,10 @@ namespace VisionBuilder.UI.Statistics
private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
{
_startTime = DateTime.Now;
_startTime = obj.SessionStarted;
_currentRecipe = obj.RecipeName;
_fileName = "tmp_"+Guid.NewGuid().ToString()+".csv";
_directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, DateTime.Now, DateTime.Now,
_directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, _startTime, _startTime,
obj.RecipeName, cameraName: _settings.CameraName);
_filePath = Path.Combine(_directoryPath, _fileName);
var headers = new[] { _settings.StartDateColumnName, _settings.StartTimeColumnName, _settings.EndDateColumnName, _settings.EndTimeColumnName, _settings.ErrorTimeColumnName, _settings.ProductColumnName, _settings.ErrorColumnName };
@@ -84,6 +86,8 @@ namespace VisionBuilder.UI.Statistics
}
ConcurrentDictionary<string, int> _defects = new();
private DateTime _endTime;
private void RecordBadImage(string defectName)
{
@@ -96,50 +100,60 @@ namespace VisionBuilder.UI.Statistics
_defects.AddOrUpdate(defectName, 1, (key, oldValue) => oldValue + 1);
}
private void WriteMonthStatistics(string filename)
private void WriteMonthStatistics()
{
// read second line to get the start time
var lines = File.ReadAllLines(filename);
var lastLine = lines[^1];
if (lines.Length < 2) return; // no data to write
var startDate = DateTime.Parse(lastLine.Split(',')[0]);
var startTime = DateTime.Parse(lastLine.Split(',')[1]);
var endDate = DateTime.Parse(lastLine.Split(',')[2]);
var endTime = DateTime.Parse(lastLine.Split(',')[3]);
// all must be non-empty
if (startDate == DateTime.MinValue || startTime == DateTime.MinValue || endDate == DateTime.MinValue || endTime == DateTime.MinValue)
var startDate = _startTime.Date;
var startTime = _startTime;
var endDate = _endTime.Date;
var endTime = _endTime;
var monthFilePath = Path.Combine(_directoryPath, MONTHLY_FILE_NAME);
var monthStatistics = new CsvStatistics(monthFilePath, [
"Date start","Time start","Date end", "Time end" ,"Product","Error type","Error count"
]);
foreach (var defect in _defects)
{
Log.Error("Invalid date or time in statistics file: " + filename);
throw new InvalidOperationException("Invalid date or time in statistics file: " + filename);
monthStatistics.WriteLine(
[startDate.ToString("d"), startTime.ToString("T"), endDate.ToString("d"), endTime.ToString("T"), _currentRecipe, defect.Key, defect.Value.ToString()]
);
}
monthStatistics.Dispose();
}
private void WriteFinalStatistics()
{
try
{
var tmpFiles = Directory.GetFiles(_directoryPath, "tmp_*");
// order by creation time
Array.Sort(tmpFiles, (x, y) => File.GetCreationTime(x).CompareTo(File.GetCreationTime(y)));
foreach (var file in tmpFiles)
{
var finalFileName = Path.Combine(_directoryPath, BY_EVENTS_FILE_NAME);
var lines = File.ReadAllLines(file);
File.AppendAllLines(finalFileName, lines.Skip(1)); // skip header
WriteMonthStatistics(file);
File.Delete(file);
}
Log.Debug("Writing month statistics");
WriteMonthStatistics();
}
catch (Exception e)
{
Log.Error("Error writing statistics for " + _settings.CameraName+ e);
Log.Error("Error writing statistics for " + _settings.CameraName+ "\n"+ e);
}
_csvStatistics?.Dispose();
_csvStatistics = null;
}
public void Initialize()
public void InitializeModule()
{
WriteFinalStatistics();
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
_recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
}
public void Dispose()
{
_csvStatistics?.Dispose();
_recognitionControl.SessionStarted -= _recognitionControl_SessionStarted;
_recognitionControl.ImageProcessed -= _recognitionControl_ImageProcessed;
_recognitionControl.SessionEnded -= _recognitionControl_SessionEnded;
}
}
}

View File

@@ -1,5 +1,6 @@
using Inspectron.Settings;
using Inspectron.Settings.Attributes;
using Lindt.Colorballs.Duo.Utils;
using VisionBuilder.UI.Common;
namespace VisionBuilder.UI.Statistics;
@@ -16,31 +17,10 @@ public class VisionBuilderStatisticsSettings: ISettings
[SettingDescription(
@"
Specifies directory, in which MaxBad and MaxGood are applied.
You can use the following system variables:
$RECIPE_NAME - name of the current recipe
$DEFECT_NAME - name of the current defect(for bad images)
$CAMERA_NAME - name of the camera
$HOUR - hour of the image
$MINUTE - minute of the image
$SECOND - second of the image
$MS - milliseconds of the image
$DAY - day of the image
$MONTH - month of the image
$YEAR - year of the image
$SS_HOUR - hour of the session start
$SS_MINUTE - minute of the session start
$SS_SECOND - second of the session start
$SS_DAY - day of the session start
$SS_MONTH - month of the session start
$SS_YEAR - year of the session start
"
"+PathSettingsUtils.INSTUCTIONS
)]
public string PathTemplate { get; set; }= @$"..\Data\Statistics\$SS_MONTH $SS_YEAR\$RECIPE_NAME\";
[SettingPreview(typeof(VisionBuilderStatisticsSettings), nameof(PathTemplatePreview))]
public string PathTemplate { get; set; }= @$"..\Data\Statistics\$SS_MONTH_NUM $SS_YEAR\$RECIPE_NAME\";
public string StartDateColumnName { get; set; } = "Start Date";
public string StartTimeColumnName { get; set; } = "Start time";
@@ -61,4 +41,10 @@ $SS_YEAR - year of the session start
settings.RegisterSimple(this,()=>this.ProductColumnName,CameraName+"/"+"Statistics",nameof(ProductColumnName));
settings.RegisterSimple(this,()=>this.ErrorColumnName,CameraName+"/"+"Statistics",nameof(ErrorColumnName));
}
public static string PathTemplatePreview(object template)
{
return PathSettingsUtils.ConvertVariables((string)template, DateTime.Now, DateTime.Now, "my recipe",
"my defect", "my camera");
}
}