Files
mapo-mono/MP.MONO.SIM/Simulator.cs
T
2022-10-03 08:58:00 +02:00

538 lines
18 KiB
C#

using MathNet.Numerics.Distributions;
using Microsoft.Extensions.Configuration;
using MP.MONO.Core.DTO;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace MachineSim
{
/// <summary>
/// Definizione parametri simulati
/// </summary>
public class Simulator
{
#region Public Fields
public List<string> currCountersRaw = new List<string>();
public List<SimDisplayDataDTO> currSimActLog = new List<SimDisplayDataDTO>();
public List<SimDisplayDataDTO> currSimEvHist = new List<SimDisplayDataDTO>();
public List<string> currSimMachStat = new List<string>();
public List<SimDisplayDataDTO> currSimMaint = new List<SimDisplayDataDTO>();
public List<SimDisplayDataDTO> currSimPar = new List<SimDisplayDataDTO>();
public List<SimDisplayDataDTO> currSimProd = new List<SimDisplayDataDTO>();
public List<SimDisplayDataDTO> currSimTools = new List<SimDisplayDataDTO>();
#endregion Public Fields
#region Public Constructors
public Simulator(string _confPath, int nMode, int nStatus, IDatabase? currRedisDb, IConfigurationRoot currAppConf)
{
confPath = _confPath;
numMode = nMode;
numStatus = nStatus;
redisDb = currRedisDb;
appConf = currAppConf;
setupConfig();
}
#endregion Public Constructors
#region Public Methods
public List<DisplayDataDTO> getActLog()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimActLog.Select(i => new DisplayDataDTO()
{
IsNumeric = i.IsNumeric,
MaxVal = i.MaxVal,
MinVal = i.MinVal,
Order = i.Order,
Title = i.Title,
Type = i.Type,
ValueNum = simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd, 1),
DisplFormat = i.DisplFormat
}).ToList();
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
}
return answ;
}
public List<String> getAlarms()
{
List<string> activeAlarms = new List<string>();
//int alarmCode = rand.Next(0, 255);
int alarmCode = rand.Next(0, 160);
// se >= 128 --> 0 (no alarm)
alarmCode = alarmCode <= 127 ? alarmCode : 0;
for (int i = 0; i < 8; i++)
{
if ((alarmCode & (1 << i)) != 0)
{
activeAlarms.Add($"Alarm {i:000}");
}
}
return activeAlarms;
}
/// <summary>
/// Restituisce elenco info dati x contatori come lista da adapter
/// </summary>
/// <returns></returns>
public Dictionary<string, string> getCountersRaw()
{
Dictionary<string, string> answ = new Dictionary<string, string>();
// simulo stato macchina ad 1 nel 99.5% (0 nel 0.5% dei casi
if (rand.Next(0, 1000) < 995)
{
answ.Add("MACHINE STATUS", "1");
}
else
{
answ.Add("MACHINE STATUS", "1");
}
// simulo stato processo in ciclo 90% dei casi, resto HOLD
if (rand.Next(0, 1000) < 900)
{
answ.Add("PROCESS STATUS", "2");
// simulo modo auto 90% dei casi
if (rand.Next(0, 1000) < 900)
{
answ.Add("PROCESS MODE", "2");
// aggiungo spindle load che sono attivi 50% del tempo ogni coppia, random
if (rand.Next(0, 1000) < 500)
{
answ.Add("SPINDLE 1 LOAD", $"{rand.Next(5, 80)}");
answ.Add("SPINDLE 2 LOAD", $"{rand.Next(5, 80)}");
answ.Add("SPINDLE 3 LOAD", $"{rand.Next(5, 80)}");
answ.Add("SPINDLE 4 LOAD", $"{rand.Next(5, 80)}");
}
else
{
answ.Add("SPINDLE 1 LOAD", "0");
answ.Add("SPINDLE 2 LOAD", "0");
answ.Add("SPINDLE 3 LOAD", "0");
answ.Add("SPINDLE 4 LOAD", "0");
}
}
else
{
answ.Add("PROCESS MODE", "1");
answ.Add("SPINDLE 1 LOAD", "0");
answ.Add("SPINDLE 2 LOAD", "0");
answ.Add("SPINDLE 3 LOAD", "0");
answ.Add("SPINDLE 4 LOAD", "0");
}
}
else
{
answ.Add("PROCESS STATUS", "3");
answ.Add("PROCESS MODE", "7");
answ.Add("SPINDLE 1 LOAD", "0");
answ.Add("SPINDLE 2 LOAD", "0");
answ.Add("SPINDLE 3 LOAD", "0");
answ.Add("SPINDLE 4 LOAD", "0");
}
return answ;
}
public List<DisplayDataDTO> getEvents()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimEvHist.Select(i => new DisplayDataDTO()
{
IsNumeric = i.IsNumeric,
MaxVal = i.MaxVal,
MinVal = i.MinVal,
Order = i.Order,
Title = i.Title,
Type = i.Type,
ValueNum = simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd, 1),
DisplFormat = i.DisplFormat
}).ToList();
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
}
return answ;
}
public List<DisplayDataDTO> getMacStats()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
var oee = new DisplayDataDTO()
{
IsNumeric = true,
MaxVal = 1,
MinVal = 0.50,
Order = 1,
Title = "OEE",
Type = "OEE",
ValueNum = simNext(.75, .50, 1, .1, .2, 1, 0.5),
DisplFormat = "P1"
};
answ.Add(oee);
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
}
return answ;
}
public List<DisplayDataDTO> getMaint()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimMaint.Select(i => new DisplayDataDTO()
{
IsNumeric = i.IsNumeric,
MaxVal = i.MaxVal,
MinVal = i.MinVal,
Order = i.Order,
Title = i.Title,
Type = i.Type,
ValueNum = simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd, i.IsBoolean ? 2 : 1, i.TrueRatio),
DisplFormat = i.DisplFormat
}).ToList();
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
}
return answ;
}
public List<DisplayDataDTO> getParameters()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimPar.Select(i => new DisplayDataDTO()
{
IsNumeric = i.IsNumeric,
MaxVal = i.MaxVal,
MinVal = i.MinVal,
Order = i.Order,
Title = i.Title,
Type = i.Type,
ValueNum = simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd, 1),
DisplFormat = i.DisplFormat,
CssIcon = i.CssIcon,
EnablePlot = i.EnablePlot,
ShowBar = i.ShowBar,
ShowGauge = i.ShowGauge,
}).ToList();
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
}
return answ;
}
public Dictionary<string, double> getParamsVal()
{
Dictionary<string, double> answ = new Dictionary<string, double>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimPar.ToDictionary(i => i.Title, i => simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd, 1));
return answ;
}
public ProductionDTO getProd()
{
int stdCycle = 5;
ProductionDTO currMachDto = new ProductionDTO()
{
Order = "ODL Test",
ItemCode = "ART.0000123",
ProgName = "P000012",
CurrQty = DateTime.Now.Minute + rand.Next(1, 40),
OrderQty = 100,
CycleTimeMin = rand.NextDouble() * stdCycle,
Message = "...simulated data..."
};
return currMachDto;
}
#if false
public List<DisplayDataDTO> getProdStats()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimMachStat.Select(i => new DisplayDataDTO()
{
IsNumeric = i.IsNumeric,
MaxVal = i.MaxVal,
MinVal = i.MinVal,
Order = i.Order,
Title = i.Title,
Type = i.Type,
ValueNum = simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd),
DisplFormat = i.DisplFormat
//Value = $"{getNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd):N3}",
}).ToList();
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
}
return answ;
}
#endif
/// <summary>
/// Restituisce elenco info stato Macchina simulato come lista da adapter
/// </summary>
/// <returns></returns>
public Dictionary<string, string> getStatus()
{
Dictionary<string, string> answ = new Dictionary<string, string>();
// simulo stato macchina ad 1 nel 99.5% (0 nel 0.5% dei casi
if (rand.Next(0, 1000) < 995)
{
answ.Add("MACHINE STATUS", "1");
}
else
{
answ.Add("MACHINE STATUS", "1");
}
// simulo stato processo in ciclo 90% dei casi, resto HOLD
if (rand.Next(0, 1000) < 900)
{
answ.Add("PROCESS STATUS", "2");
// simulo modo auto 90% dei casi
if (rand.Next(0, 1000) < 900)
{
answ.Add("PROCESS MODE", "2");
}
else
{
answ.Add("PROCESS MODE", "1");
}
}
else
{
answ.Add("PROCESS STATUS", "3");
answ.Add("PROCESS MODE", "7");
}
// aggiunta part program che cambia ogni minuto...
answ.Add("RECIPE NAME", $"{DateTime.Now:yyMMdd-HHmm}.prg");
return answ;
}
public List<DisplayDataDTO> getTools()
{
List<DisplayDataDTO> answ = new List<DisplayDataDTO>();
// genero a partire dall'elenco configurato da simulare...
answ = currSimTools.Select(i => new DisplayDataDTO()
{
IsNumeric = i.IsNumeric,
MaxVal = i.MaxVal,
MinVal = i.MinVal,
Order = i.Order,
Title = i.Title,
Type = i.Type,
ValueNum = simNext(i.ValueNum, i.MinVal, i.MaxVal, i.SimMean, i.SimStd, 3),
DisplFormat = i.DisplFormat,
EnablePlot = i.EnablePlot,
ShowBar = i.ShowBar,
ShowGauge = i.ShowGauge,
CssIcon = i.CssIcon,
HLShow = i.HLShow
}).ToList();
foreach (var item in answ)
{
item.Value = $"{item.ValueNum.ToString(item.DisplFormat)}";
// aggiorno con ultimo valore simulato...
var simItem = currSimTools.FirstOrDefault(x => x.Title == item.Title);
if (simItem != null)
{
simItem.ValueNum = item.ValueNum;
}
}
return answ;
}
/// <summary>
/// Calcola prox valore simulato dati i parametri rand configurati, con innovazione
/// stocastica + deterministica:
/// - ciclo ogni 4 minuti - con operazione modulo (4)
/// * 0 fermo
/// * 1 --&gt; sale
/// * 2 fermo
/// * 3 --&gt; scende
/// </summary>
/// <param name="CurrVal"></param>
/// <param name="MinVal"></param>
/// <param name="MaxVal"></param>
/// <param name="sMean"></param>
/// <param name="sStd"></param>
/// <param name="simMode">Modalità simulazione (1-3)
/// 1 = std, RandomWalk + trend periodico con memoria
/// 2 = boolean
/// 3 = trend secondo valore mean (crescente se >0, decrescente se <0)
/// </param>
/// <param name="trueRatio"></param>
/// <returns></returns>
public double simNext(double CurrVal, double MinVal, double MaxVal, double sMean, double sStd, int simMode = 1, double trueRatio = 0.5)
{
double innov = 0;
double trend = 0;
// se è boolean --> simulazione 0/1 rapida con % ratio indicata
switch (simMode)
{
case 2:
CurrVal = rand.Next(10000) <= trueRatio * 10000 ? 1 : 0;
break;
case 3:
innov = Normal.Sample(sMean, sStd);
trend = rand.NextDouble() * 2 * sMean;
double delta = innov + trend;
// il delta deve essere dello stesso segno della media...
if (delta * sMean < 0)
{
delta = delta * -1;
}
CurrVal += delta;
// verifico estremi...
CurrVal = CurrVal > MaxVal ? MinVal : CurrVal;
CurrVal = CurrVal < MinVal ? MaxVal : CurrVal;
break;
case 1:
default:
// innovazione stocastica di base
innov = Normal.Sample(sMean, sStd);
int multFact = rand.Next(1, 6);
// innovazione deterministica:
int resto = (DateTime.Now.Second % 10);
trend = 0;
switch (resto)
{
case 0:
case 1:
case 7:
trend = sMean + multFact * sStd;
break;
case 2:
case 5:
case 6:
trend = -(sMean + multFact * sStd);
break;
default:
break;
}
CurrVal += innov + trend;
// verifico estremi...
CurrVal = CurrVal > MaxVal ? MaxVal : CurrVal;
CurrVal = CurrVal < MinVal ? MinVal : CurrVal;
break;
}
return CurrVal;
}
#endregion Public Methods
#region Protected Fields
protected string confPath = "";
protected Random rand = new Random();
#endregion Protected Fields
#region Protected Properties
protected IConfigurationRoot appConf { get; set; } = null!;
protected int numMode { get; set; } = 1;
protected int numStatus { get; set; } = 1;
protected IDatabase redisDb { get; set; } = null!;
#endregion Protected Properties
#region Private Methods
private void setupConfDTO(string paramFileName, ref List<SimDisplayDataDTO> localObj)
{
string fullPath = Path.Combine(confPath, paramFileName);
if (File.Exists(fullPath))
{
var rawData = File.ReadAllText(fullPath);
if (!string.IsNullOrEmpty(rawData))
{
var deserObj = JsonConvert.DeserializeObject<List<SimDisplayDataDTO>>(rawData);
if (deserObj != null)
{
localObj = deserObj;
}
}
}
}
private void setupConfig()
{
// setup conf Parametri
setupConfDTO("SimParams.json", ref currSimPar);
// setup conf Maintenance
setupConfDTO("SimMaint.json", ref currSimMaint);
// setup conf Tools
setupConfDTO("SimTools.json", ref currSimTools);
// setup conf Event History
setupConfDTO("SimEvHist.json", ref currSimEvHist);
// setup conf Activity Log
setupConfDTO("SimActLog.json", ref currSimActLog);
// setup conf Prod
setupConfDTO("SimProd.json", ref currSimProd);
// setup conf MachStat
setupConfList("SimMachStat.json", ref currSimMachStat);
// setup conf Prod
setupConfList("SimStatus.json", ref currCountersRaw);
}
private void setupConfList(string paramFileName, ref List<string> localObj)
{
string fullPath = Path.Combine(confPath, paramFileName);
if (File.Exists(fullPath))
{
var rawData = File.ReadAllText(fullPath);
if (!string.IsNullOrEmpty(rawData))
{
var deserObj = JsonConvert.DeserializeObject<List<String>>(rawData);
if (deserObj != null)
{
localObj = deserObj;
}
}
}
}
#endregion Private Methods
}
}