Files
Mapo-IOB-WIN/IOB-WIN-WS/IobWs/RestBase.cs
T
2026-05-23 21:24:55 +02:00

647 lines
24 KiB
C#

using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using IOB_UT_NEXT.Objects;
using IOB_UT_NEXT.Services.Files;
using MapoSDK;
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace IOB_WIN_WS.IobWs
{
/// <summary>
/// Adapter base per sviluppo chiamate con servizi REST
/// </summary>
public class RestBase : Iob.GenericNext
{
#region Public Constructors
/// <summary>
/// Costruttore dell'IOB Rest generico
/// </summary>
/// <param name="caller">Form chiamante</param>
/// <param name="IOBConf">Configurazione (legacy)</param>
/// <param name="IobConfFull">Configurazione (v 4.x)</param>
public RestBase(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
{
lgInfo($"Richiesto Adapter IobRest.Base con i parametri seguenti | ADDR: {IOBConfFull.Device.Connect.IpAddr} | PORT: {IOBConfFull.Device.Connect.Port}");
DtHelp.lastPING = DateTime.Now.AddHours(-1);
// predispongo configurazione specifica Rest...
if (!string.IsNullOrEmpty(getOptPar("REST_CONF")))
{
setupRestConf(getOptPar("REST_CONF"));
}
}
#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, string codTav)
{
/*---------------------------------------
* 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 false
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!");
}
}
#endif
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>();
// indico esecuzione e proseguo
DtHelp.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)
{
DateTime adesso = DateTime.Now;
DtHelp.lastReadPLC = adesso;
// verifico non sia in veto invio iniziale...
if (queueInEnabCurr)
{
try
{
currDispData.semIn = Semaforo.SV;
// effettua refresh dati da leggere SPECIFICi x citizen...
refreshData();
// decodifica e gestione
decodeToBaseBitmap(ref currDispData);
// display
reportRawInput(ref currDispData);
}
catch (Exception exc)
{
currDispData.semIn = Semaforo.SR;
lgError($"Eccezione in readSemafori:{Environment.NewLine}{exc}");
}
}
else
{
lgDebug($"[VETO readSemafori] | veto attivo alle {adesso:yyyy.MM.dd HH:mm:ss}");
checkVetoQueueIn();
}
}
/// <summary>
/// Override connessione
/// </summary>
public override void tryConnect()
{
if (!connectionOk)
{
// controllo che il ping sia stato tentato almeno pingTestSec fa...
if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec"))
{
if (verboseLog || periodicLog)
{
lgInfo("Rest: ConnKO - tryConnect");
}
// in primis salvo data ping...
DtHelp.lastPING = DateTime.Now;
// se passa il ping faccio il resto...
if (testPingMachine == IPStatus.Success)
{
string szStatusConnection = "";
try
{
// ora provo connessione...
parentForm.commPlcActive = true;
// chiamo metodo connect
var checkResp = ExecuteCallGet(GetUrlResource("GetConnection"), true);
// forse va eliminato...
lgInfo($"GetConnection | {checkResp} (Base.tryConnect)");
if (checkResp != null && checkResp.ToLower().Contains("true"))
{
connectionOk = true;
}
else
{
lgError($"Errore check connessione | checkResp: {checkResp}");
}
// 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 IobRest.Base: {szStatusConnection}{Environment.NewLine}{exc}");
connectionOk = false;
lgInfo($"Eccezione in TryConnect, Adapter IobRest.Base 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: Rest controllo PING fallito per IP {IOBConfFull.Device.Connect.PingIpAddr}");
}
}
}
}
else
{
needRefresh = true;
}
}
public override void tryDisconnect()
{
// registro solo che è disconnesso
connectionOk = false;
queueInEnabCurr = false;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Api Url di base x chiamate REST
/// </summary>
protected string apiUrl = "http://localhost:8733";
/// <summary>
/// Num err connessioni corrente
/// </summary>
protected int connErrCur = 0;
/// <summary>
/// Max num err connesisone prima di disconnettere
/// </summary>
protected int connErrMax = 5;
/// <summary>
/// LookUpTable informazioni raccolte
/// </summary>
protected Dictionary<string, string> RestDataLUT = new Dictionary<string, string>();
/// <summary>
/// Timeout chiamate REST
/// </summary>
protected int tOutSec = 60;
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Parametri specifici Client Rest
/// </summary>
protected RestParamConf restParams { get; set; } = new RestParamConf();
#endregion Protected Properties
#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)
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected virtual void decodeToBaseBitmap(ref newDisplayData currDispData)
{
// init a zero...
B_input = 0;
if (queueInEnabCurr)
{
/* -----------------------------------------------------
* bitmap MAPO STD 60
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: allarme TCiclo (SLOW)
* B6: WarmUp_CoolDown
* B7: EmergArmed (1 = NON emergenza, 0 = emergenza)
----------------------------------------------------- */
#if false
// per prima cosa controllo ping e se sia connesso...
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;
}
#endif
}
else
{
lgDebug($"[VETO getDataItemValue] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}");
}
}
/// <summary>
/// Esecuzione chiamata Rest tipo GET
/// </summary>
/// <param name="resource">URI da chiamare</param>
/// <param name="fastCall">Chiamata rapida (timeout rapido = 5sec)</param>
/// <returns></returns>
protected string ExecuteCallGet(string resource, bool fastCall = false)
{
string answ = "";
if (!string.IsNullOrEmpty(resource))
{
if (fastCall)
{
restOptStd.Timeout = TimeSpan.FromSeconds(5);
}
try
{
// client chiamate rest
using (var client = new RestClient(restOptStd))
{
var actReq = new RestRequest(resource, Method.Get);
// effettuo vera chiamata
var currResp = client.Get(actReq);
if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null)
{
answ = currResp.Content ?? "";
}
}
}
catch (Exception exc)
{
lgError($"Eccezione in ExecuteCallGet{Environment.NewLine}{exc}");
}
}
return answ;
}
/// <summary>
/// Esecuzione chiamata Rest tipo POST
/// </summary>
/// <param name="resource">URI da chiamare</param>
/// <param name="payload">Payload da allegare (già serializzato)</param>
/// <param name="fastCall">Chiamata rapida (timeout rapido = 5sec)</param>
/// <returns></returns>
protected string ExecuteCallPost(string resource, string payload, bool fastCall = false)
{
string answ = "";
if (!string.IsNullOrEmpty(resource))
{
if (fastCall)
{
restOptStd.Timeout = TimeSpan.FromSeconds(5);
}
try
{
// client chiamate rest
using (var client = new RestClient(restOptStd))
{
var actReq = new RestRequest(resource, Method.Post);
actReq.AddJsonBody(payload);
// effettuo vera chiamata
var currResp = client.Post(actReq);
if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null)
{
answ = currResp.Content ?? "";
}
}
}
catch (Exception exc)
{
lgError($"Eccezione in ExecuteCallPost{Environment.NewLine}{exc}");
}
}
return answ;
}
/// <summary>
/// restituisce URL della risorsa eventualmente completato con token o altro
/// </summary>
/// <param name="resName"></param>
/// <returns></returns>
protected string GetUrlResource(string resName)
{
string answ = "";
if (restParams != null && restParams.CallList != null && restParams.CallList.Count > 0)
{
if (restParams.CallList.ContainsKey(resName))
{
answ = restParams.CallList[resName].Url;
// eseguo eventuale sostituzione (se trovo "[[")
if (answ.Contains("[["))
{
string pattern = @"\[\[.*\]\]";
Regex regex = new Regex(pattern);
Match match = regex.Match(answ);
if (match.Success)
{
string key = match.Value.Replace("[[", "").Replace("]]", "");
// cerco nella LUT...
if (RestDataLUT.ContainsKey(key))
{
// sostituisco!
answ = answ.Replace($"[[{key}]]", RestDataLUT[key]);
}
}
}
}
}
return answ;
}
/// <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 volta...
upsertKey(item.uid, item.value);
#if false
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 Rest | {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 = "";
}
}
#endif
}
}
/// <summary>
/// Esegue lettura dati + salvataggio in LUT
/// </summary>
protected virtual void refreshData()
{ }
/// <summary>
/// Recupero valore da cache LUT
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
protected string RestLutGet(string key)
{
string value = "";
if (RestDataLUT.ContainsKey(key))
{
value = RestDataLUT[key];
}
return value;
}
/// <summary>
/// Upsert in cache LUT del parametro
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
protected void RestLutUpsert(string key, string value)
{
if (RestDataLUT.ContainsKey(key))
{
RestDataLUT[key] = value;
}
else
{
RestDataLUT.Add(key, value);
}
}
/// <summary>
/// Upsert in cache LUT del parametro
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
protected void RestLutUpsert(string key, object value)
{
if (RestDataLUT.ContainsKey(key))
{
RestDataLUT[key] = $"{value}";
}
else
{
RestDataLUT.Add(key, $"{value}");
}
}
protected void setupRestConf(string restConfFile)
{
// se ho file specifico
if (!string.IsNullOrEmpty(getOptPar("REST_CONF")))
{
string rawData = "";
// leggo file e decodifico...
string confPath = $"{Application.StartupPath}/DATA/CONF/{restConfFile}";
lgInfo($"Apertura file {confPath}");
using (StreamReader sr = new StreamReader(confPath))
{
rawData = sr.ReadToEnd().Replace("\n", "").Replace("\r", "");
}
// continuo decodifica
if (!string.IsNullOrEmpty(rawData))
{
restParams = JsonConvert.DeserializeObject<RestParamConf>(rawData);
if (restParams != null)
{
// inizializzo dal file di conf le variabili necessarie...
tOutSec = restParams.timeOutSec;
apiUrl = restParams.apiUrl ?? "http://localhost:8733";
// sistemo parametri specali come samplePeriod ed errori conn...
samplePeriod = restParams.samplePeriod;
connErrMax = restParams.connErrorMax;
}
}
}
// sistemo conf standard x call REST
restOptStd = new RestClientOptions
{
Timeout = TimeSpan.FromSeconds(tOutSec),
BaseUrl = new Uri(apiUrl)
};
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// Conf client RestSharp standard:
/// - timeout 1 min
/// </summary>
private RestClientOptions restOptStd = new RestClientOptions { Timeout = TimeSpan.FromSeconds(60) };
#endregion Private Fields
}
}