Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobSoap/Gomba.cs
T
Samuele Locatelli 8bb0f158b5 SPLIT PROGETTO!!!
- proj di base con le 2 form da ereditare
- progetto globale che contiene TUTTI gli adapter (pronto a venire spezzettato
- gettate le basi x "portare fuori" i vari componenti oppure fare compilazione condizonale
2024-12-20 10:16:32 +01:00

740 lines
30 KiB
C#

using EgwProxy.Gomba.GombaServ;
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net.NetworkInformation;
namespace IOB_WIN_NEXT.IobSoap
{
/// <summary>
/// Adapter specializzato per GOMA e le chiamate tramite WS Soap alla bilancia, libreria EgwProxy.Gomba
/// </summary>
public class Gomba : Iob.GenericNext
{
#region Public Constructors
/// <summary>
/// Costruttore dell'IOB SOAP della bilancia GOMBA
/// </summary>
/// <param name="caller">AdapterForm chiamante</param>
/// <param name="IOBConf">Configurazione IOB per avvio</param>
public Gomba(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo($"Richiesto Adapter IobSoap.Gomba con i parametri seguenti | ADDR: {IOBConf.cncIpAddr} | PORT: {IOBConf.cncPort}");
redKeyPesate = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:ListPesate");
lastPING = DateTime.Now.AddHours(-1);
// verifico se sia attiva gestione riduzione FluxLog...
if (!string.IsNullOrEmpty(getOptJsonKVP("numLastWeight")))
{
int.TryParse(getOptJsonKVP("numLastWeight"), out numLastWeight);
lgInfo($"Gomba: setup specifico | numLastWeight: {numLastWeight}");
}
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Implementazione custom esecuzione task specifici
/// </summary>
/// <param name="task2exe"></param>
/// <returns></returns>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
/*---------------------------------------
* gestione execute task SPECIFICI x pesa:
* - salva i parametri richiesta (RM, cod1..cod6)
* - esegue metodo richiesta (IN/OUT)
*---------------------------------------*/
// Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti...
Dictionary<string, string> taskDone = new Dictionary<string, string>();
if (task2exe != null)
{
lgTrace($"executeTasks: richiesta esecuzione {task2exe.Count} task");
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici: qui sono NON standard...
foreach (var item in task2exe)
{
lgInfo($"TASK | {item.Key} --> {item.Value}");
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setParameter:
// richiedo da URL i parametri WRITE da popolare
lgInfo("Chiamata setParameter --> processMemWriteRequests");
taskVal = processMemWriteRequests();
// se restituiscce "" faccio altra prova...
if (string.IsNullOrEmpty(taskVal))
{
// i parametri me li aspetto come stringa composta paramName|paramvalue
if (item.Value.Contains("|"))
{
string[] paramsJob = item.Value.Split('|');
taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
}
else
{
taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
}
}
break;
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
}
else
{
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
}
}
return taskDone;
}
/// <summary>
/// Recupero dati dinamici...
/// </summary>
public override Dictionary<string, string> getDynData()
{
// valore non presente in vers default... se gestito fare override
Dictionary<string, string> outVal = new Dictionary<string, string>();
// controllo se ho indicato ancora che ci siano pesate già lette da inviare...
if (num2send() > 0)
{
// preparo oggetti x confronto ...
List<WeightRec> listaArch = listPesateArchivio;
// se non ho nulla in archivio prendo ultima pesata (essendo DESC è la prima cronologicamente)
if (listaArch.Count == 0)
{
WeightRec lastRec = listPesateCurr.LastOrDefault();
// aggiungo...
SavePesata(ref outVal, lastRec);
}
// ora il confronto è cronologico sulle pesate + recenti...
else
{
// prendo prima della lista pesate archiviate = più recente come dt da cui partire...
DateTime dtRif = listaArch.FirstOrDefault().DtEvent;
var firstRec = listPesateCurr
.Where(x => x.DtEvent > dtRif)
.OrderBy(x => x.DtEvent)
.FirstOrDefault();
if (firstRec != null)
{
SavePesata(ref outVal, firstRec);
}
}
// processo comunque le aree memoria READ...
if (memMap.mMapRead.Count > 0)
{
foreach (var item in memMap.mMapRead)
{
var currVal = getCurrProdData(item.Key, "");
if (!outVal.ContainsKey(item.Key))
{
outVal.Add(item.Key, $"{currVal}");
}
// se fosse logReq --> resetto...
if (item.Key == "logReq" && !string.IsNullOrEmpty(currVal))
{
upsertKey(item.Key, "");
}
}
}
}
// altrimenti rileggo e cerco se ci siano
else
{
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
// test lettura elenco pesate... se NON nullo --> OK!
var weightArray = gombaConn.reqWeightList("ALL", dataFrom, dataTo);
// riordino DESC
listPesateCurr = WeightRec.ConvertPesate(weightArray.OrderByDescending(x => x.dateIn).ToList());
}
catch (Exception exc)
{
lgError($"Eccezione in gombaConn.reqWeightList{Environment.NewLine}{exc}");
}
sw.Stop();
lgInfo($"getDynData | SOAP: effettuata chiamata reqWeightList in {sw.Elapsed.TotalMilliseconds}ms | {dataFrom} --> {dataTo} | {listPesateCurr.Count} rec");
}
// indico esecuzione e proseguo
lastReadPLC = DateTime.Now;
return outVal;
}
/// <summary>
/// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
/// aggiornare x display in form</paramref>
/// </summary>
public override void readSemafori(ref newDisplayData currDispData)
{
if (connectionOk)
{
B_input = 1;
currDispData.semIn = Semaforo.SV;
// se ho pesate in memoria nel periodo richiesto --> RUN
if (listPesateCurr != null && listPesateCurr.Count > 0)
{
B_input += (1 << 1);
}
// metto manuale
else
{
B_input += (1 << 4);
}
// accodo NON emergenza
B_input += (1 << 7);
}
else
{
B_input = 0;
currDispData.semIn = Semaforo.SR;
}
}
/// <summary>
/// Override connessione
/// </summary>
public override void tryConnect()
{
if (!connectionOk)
{
// controllo che il ping sia stato tentato almeno pingTestSec fa...
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
{
if (verboseLog || periodicLog)
{
lgInfo("Gomba: ConnKO - tryConnect");
}
// in primis salvo data ping...
lastPING = DateTime.Now;
// se passa il ping faccio il resto...
if (testPingMachine == IPStatus.Success)
{
string szStatusConnection = "";
try
{
// ora provo connessione...
parentForm.commPlcActive = true;
// init del proxy!!!
string endpointAddr = $"https://{cIobConf.cncIpAddr}:{cIobConf.cncPort}/ws";
lgInfo($"Tentativo avvio SOAP su endpoint {endpointAddr}");
Stopwatch sw = new Stopwatch();
sw.Start();
// salto verifica certificato
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
// riprovo connessione SE non fosse andata...
int maxTry = 5;
int nTry = 1;
// tento avvio WS SOAP!
gombaConn = new EgwProxy.Gomba.GombaServ.lwpServiceClient("lwpServicePort", new System.ServiceModel.EndpointAddress(endpointAddr));
while (gombaConn == null || (nTry <= maxTry && !checkStateOk(gombaConn.State)))
{
lgInfo($"Tentativo connessione Gomba: | endpointAddr: {endpointAddr} | nTry: {nTry}");
// tento avvio WS SOAP!
gombaConn = new EgwProxy.Gomba.GombaServ.lwpServiceClient("lwpServicePort", new System.ServiceModel.EndpointAddress(endpointAddr));
nTry++;
}
if (checkStateOk(gombaConn.State))
{
connectionOk = true;
List<gestWeightOut> weightArray = new List<gestWeightOut>();
try
{
// test lettura elenco pesate... se NON nullo --> OK!
weightArray = gombaConn.reqWeightList("ALL", dataFrom, dataTo).OrderByDescending(x => x.dateIn).ToList();
// riordino DESC
listPesateCurr = WeightRec.ConvertPesate(weightArray);
}
catch (Exception exc)
{
lgError($"Eccezione in gombaConn.reqWeightList{Environment.NewLine}{exc}");
}
sw.Stop();
lgInfo($"SOAP: effettuata chiamata connessione + reqWeightList in {sw.Elapsed.TotalMilliseconds}ms | {dataFrom} --> {dataTo}");
if (listPesateCurr != null && listPesateCurr.Count >= 0)
{
lgInfo($"szStatusConnection Gomba, recuperato elenco di {listPesateCurr.Count} pesate");
parentForm.commPlcActive = false;
connectionOk = true;
// sistemo data da cui iniziare recuperi successivi se ho + di numLastWeight
if (weightArray.Count > numLastWeight)
{
var lastRec = weightArray
.OrderByDescending(x => x.dateIn)
.Skip(numLastWeight).FirstOrDefault();
// metto giorno antecedente
dtStartLive = lastRec.dateIn.Date.AddDays(-1);
}
// processo pesate!
demFactDynData = 1;
processDynData();
}
}
// refresh stato connessione!!!
if (connectionOk)
{
queueInEnabCurr = true;
if (adpRunning)
{
lgInfo("Connessione OK");
}
}
else
{
lgError("Impossibile procedere, connessione mancante...");
}
}
catch (Exception exc)
{
lgFatal($"Errore nella connessione all'adapter Gomba: {szStatusConnection}{Environment.NewLine}{exc}");
connectionOk = false;
lgInfo($"Eccezione in TryConnect, Adapter Gomba NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
}
}
else
{
// loggo no risposta ping ...
connectionOk = false;
if (verboseLog || periodicLog)
{
lgInfo($"Attenzione: Gomba controllo PING fallito per IP {cIobConf.cncPingAddr}");
}
}
}
}
else
{
needRefresh = true;
}
}
public override void tryDisconnect()
{
// registro solo che è disconnesso
connectionOk = false;
queueInEnabCurr = false;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Proxy per connessione al SOAP webservice GOMBA
/// </summary>
protected EgwProxy.Gomba.GombaServ.lwpServiceClient gombaConn;
protected int numLastWeight = 100;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// verifica stato ok ovvero connected oppure open
/// </summary>
/// <param name="currState"></param>
/// <returns></returns>
protected bool checkStateOk(System.ServiceModel.CommunicationState currState)
{
return currState == System.ServiceModel.CommunicationState.Opened || currState == System.ServiceModel.CommunicationState.Created;
}
/// <summary>
/// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
lgTrace($"plcWriteParams: richiesta per {updatedPar.Count} params");
foreach (var item in updatedPar)
{
lgInfo($"ITEM | {item.uid} | {item.value}");
// salvo i valori di setup x prox pesata...
upsertKey(item.uid, item.value);
bool fatto = false;
bool isPesata = false;
bool isIN = false;
gestWeightOut answ = new gestWeightOut();
// se è richiesta pesata IN/OUT --> mando chiamata
if (item.uid == "reqPesata")
{
isPesata = true;
isIN = item.reqValue.ToUpper() == "IN";
//isIN = item.value.ToUpper() == "IN";
try
{
answ = reqWeight(isIN);
}
catch (Exception exc)
{
lgError($"Eccezione in plcWriteParams.reqWeight | isIn: {isIN}{Environment.NewLine}{exc}");
}
}
else if (item.uid == "RM" || item.uid.StartsWith("Cod"))
{
// comunque segno fatto x altri casi
fatto = true;
}
// se è pesata...
if (isPesata)
{
// se è OK
if (answ.feedback == "C")
{
lgInfo($"reqWeight | Effettuato richiesta | {answ.feedback} | {answ.notes}");
// resetto pesata
upsertKey(item.uid, "");
}
else
{
lgError($"reqWeight | Errore in richiesta peso GOMBA | {answ.feedback} | {answ.notes}");
}
item.value = "";
item.reqValue = "";
item.lastRead = DateTime.Now;
item.UM = "";
// salvo esito richiesta comunque
upsertKey("logReq", $"{answ.feedback} | {answ.notes}");
// faccio in modo di eseguire subito getDynData
demFactDynData = 1;
processDynData();
}
else
{
// se fatto --> aggiorno!
if (fatto)
{
//item.value = item.reqValue;
item.reqValue = "";
item.lastRead = DateTime.Now;
item.UM = "";
}
}
}
}
/// <summary>
/// Esegue richiesta PESO
/// </summary>
/// <param name="reqIN">Tipo richiesta: IN (true) / OUT (false)</param>
/// <returns></returns>
protected gestWeightOut reqWeight(bool reqIN)
{
lgInfo($"reqWeight | IN: {reqIN}");
gestWeightOut answ = null;
try
{
Stopwatch sw = new Stopwatch();
sw.Start();
// preparo parametri
string tipoRic = reqIN ? "IN" : "OUT";
string rm = getCurrProdData("RM", $"{DateTime.Now:yyyyMMdd-HHmmss}");// string.IsNullOrEmpty(currProdData["RM"]) ? $"{DateTime.Now:yyyyMMdd-HHmmss}" : currProdData["RM"];
string Cod1 = getCurrProdData("Cod1", ""); //string.IsNullOrEmpty(currProdData["Cod1"]) ? "" : currProdData["Cod1"];
string Cod2 = getCurrProdData("Cod2", ""); //string.IsNullOrEmpty(currProdData["Cod2"]) ? "" : currProdData["Cod2"];
string Cod3 = getCurrProdData("Cod3", ""); //string.IsNullOrEmpty(currProdData["Cod3"]) ? "" : currProdData["Cod3"];
string Cod4 = getCurrProdData("Cod4", ""); //string.IsNullOrEmpty(currProdData["Cod4"]) ? "" : currProdData["Cod4"];
string Cod5 = getCurrProdData("Cod5", ""); //string.IsNullOrEmpty(currProdData["Cod5"]) ? "" : currProdData["Cod5"];
string Cod6 = getCurrProdData("Cod6", ""); //string.IsNullOrEmpty(currProdData["Cod6"]) ? "" : currProdData["Cod6"];
// faccio chiamata
answ = gombaConn.memWeight(tipoRic, rm, Cod1, Cod2, Cod3, Cod4, Cod5, Cod6);
sw.Stop();
lgInfo($"reqWeight: effettuata chiamata SOAP in {sw.Elapsed.TotalMilliseconds}ms | {dataFrom} --> {dataTo}");
}
catch (Exception exc)
{
lgError($"reqWeight | errore richiesta pesatura{Environment.NewLine}{exc}");
}
return answ;
}
protected void SavePesata(ref Dictionary<string, string> currDict, WeightRec newRec)
{
DictUpsert(ref currDict, "RM", newRec.RM ?? "");
string tag = newRec.isIn ? "In" : "Out";
DictUpsert(ref currDict, $"lastWeight{tag}", $"{newRec.weight:N2}");
// registro record completo ultima pesata
DictUpsert(ref currDict, $"lastRec{tag}", formatPesata(newRec));
// la aggiungo alle pesate archiviate...
AppendPesata(newRec);
}
#endregion Protected Methods
#region Protected Classes
protected class WeightRec
{
#region Public Properties
public string cod1 { get; set; } = "";
public string cod2 { get; set; } = "";
public string cod3 { get; set; } = "";
public string cod4 { get; set; } = "";
public string cod5 { get; set; } = "";
public string cod6 { get; set; } = "";
public DateTime DtEvent { get; set; } = DateTime.Today;
public bool isIn { get; set; } = true;
public string RM { get; set; } = "ND";
public double weight { get; set; } = 0;
#endregion Public Properties
#region Public Methods
/// <summary>
/// Effettua converione come lista cronologica pesate (singoli record IN/OUT)
/// </summary>
/// <param name="pesateOrig"></param>
/// <returns></returns>
public static List<WeightRec> ConvertPesate(List<gestWeightOut> pesateOrig)
{
List<WeightRec> answ = new List<WeightRec>();
foreach (var item in pesateOrig)
{
// verifico se ci sia record IN...
if (item.dateInSpecified)
{
var newRec = new WeightRec()
{
RM = item.rm,
DtEvent = item.dateIn,
isIn = true,
cod1 = item.cod1,
cod2 = item.cod2,
cod3 = item.cod3,
cod4 = item.cod4,
cod5 = item.cod5,
cod6 = item.cod6,
weight = double.Parse(item.weightIn, NumberStyles.Any, CultureInfo.InvariantCulture)
};
answ.Add(newRec);
}
// verifico se ci sia record IN...
if (item.dateOutSpecified)
{
var newRec = new WeightRec()
{
RM = item.rm,
DtEvent = item.dateOut,
isIn = false,
cod1 = item.cod1,
cod2 = item.cod2,
cod3 = item.cod3,
cod4 = item.cod4,
cod5 = item.cod5,
cod6 = item.cod6,
weight = double.Parse(item.weightOut, NumberStyles.Any, CultureInfo.InvariantCulture)
};
answ.Add(newRec);
}
}
// riordino...
answ = answ.OrderByDescending(x => x.DtEvent).ToList();
// restituisco!
return answ;
}
#endregion Public Methods
}
#endregion Protected Classes
#region Private Properties
/// <summary>
/// Data inizio periodo dati Bilancia:
/// - al boot impostato a -6 mesi
/// - dopo lettura impostato a data ultimi 5 record
/// </summary>
private string dataFrom
{
get => dtStartLive.ToString("dd/MM/yyyy");
}
/// <summary>
/// Data fine periodo dati Bilancia: oggi + 1gg
/// </summary>
private string dataTo
{
get => DateTime.Today.AddDays(1).ToString("dd/MM/yyyy");
}
/// <summary>
/// Data riferimento x avvio processo controllo
/// </summary>
private DateTime dtStartLive { get; set; } = DateTime.Today.AddMonths(-6);
/// <summary>
/// Gestione archivio serializzato delle pesate già processate
/// </summary>
private List<WeightRec> listPesateArchivio
{
get
{
List<WeightRec> answ = new List<WeightRec>();
string redKey = $"{redKeyPesate}:ALL";
string rawData = redisMan.getRSV(redKey);
if (!string.IsNullOrEmpty(rawData))
{
try
{
answ = JsonConvert.DeserializeObject<List<WeightRec>>(rawData);
lgInfo($"Rilettura status listPesateArchivio: trovati {answ.Count} record");
answ = answ.OrderByDescending(x => x.DtEvent).ToList();
}
catch (Exception exc)
{
lgError($"Errore in deserializzazione listPesateArchivio{Environment.NewLine}{exc}");
answ = new List<WeightRec>();
}
}
// rendo
return answ;
}
set
{
// riordino prima di procedere...
var tempList = value.OrderByDescending(x => x.DtEvent).ToList();
// ...trimmo se fossero + di xxx record...
if (tempList.Count > numLastWeight)
{
tempList = tempList.Take(numLastWeight).ToList();
}
string redKey = $"{redKeyPesate}:ALL";
string rawVal = JsonConvert.SerializeObject(tempList);
redisMan.setRSV(redKey, rawVal);
lgInfo($"Salvataggio status listPesateArchivio | {tempList.Count} record");
}
}
/// <summary>
/// Elenco pesate attuali
/// </summary>
private List<WeightRec> listPesateCurr { get; set; } = new List<WeightRec>();
private string redKeyPesate { get; set; } = "";
#endregion Private Properties
#region Private Methods
private static string formatCode(string currCode)
{
string code = string.IsNullOrEmpty(currCode) ? "-" : currCode;
return $" | {code}";
}
/// <summary>
/// Formatta record completo pesata
/// </summary>
/// <param name="rec">record completo pesate Gomba</param>
/// <param name="isIN">Indica se deve formattare i dati tipo IN (true) o OUT (false)</param>
/// <returns></returns>
private static string formatPesata(WeightRec rec)
{
string currVal = $"RM: {rec.RM} | ";
try
{
if (rec.isIn)
{
currVal += $"IN | ";
}
else
{
currVal += $"OUT | ";
}
currVal += $"{rec.DtEvent:yyyy-MM-dd HH:mm:ss} | {rec.weight:N2} kg";
}
catch
{ }
currVal += formatCode(rec.cod1);
currVal += formatCode(rec.cod2);
currVal += formatCode(rec.cod3);
currVal += formatCode(rec.cod4);
currVal += formatCode(rec.cod5);
currVal += formatCode(rec.cod6);
return currVal;
}
private void AppendPesata(WeightRec newRec)
{
var tmpPesate = listPesateArchivio;
tmpPesate.Add(newRec);
listPesateArchivio = tmpPesate;
}
/// <summary>
/// Calcola numero pesate da inviare confrontando lista curr ed arch
/// </summary>
private int num2send()
{
int answ = 0;
if (listPesateCurr.Count > 0)
{
var listaArch = listPesateArchivio.OrderByDescending(x => x.DtEvent).ToList();
// se NON ne ho in archivio --> tutte!
if (listaArch.Count == 0)
{
answ = listPesateCurr.Count;
}
// altrimenti solo nuove
else
{
// prendo prima della lista pesate archiviate = più recente come dt da cui partire...
DateTime dtRif = listaArch.FirstOrDefault().DtEvent;
answ = listPesateCurr
.Where(x => x.DtEvent > dtRif)
.Count();
}
}
return answ;
}
#endregion Private Methods
}
}