647 lines
24 KiB
C#
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}");
|
|
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
|
|
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;
|
|
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(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
|
|
{
|
|
if (verboseLog || periodicLog)
|
|
{
|
|
lgInfo("Rest: 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;
|
|
|
|
// 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
|
|
}
|
|
} |