using IOB_UT_NEXT; 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_NEXT.IobRest { /// /// Adapter base per sviluppo chiamate con servizi REST /// public class Base : Iob.Generic { #region Public Constructors /// /// Costruttore dell'IOB Rest generico /// /// AdapterForm chiamante /// Configurazione IOB per avvio public Base(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { lgInfo($"Richiesto Adapter IobRest.Base con i parametri seguenti | ADDR: {IOBConf.cncIpAddr} | PORT: {IOBConf.cncPort}"); 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 /// /// Implementazione custom esecuzione task specifici /// /// /// public override Dictionary executeTasks(Dictionary 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 taskDone = new Dictionary(); #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; } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); // indico esecuzione e proseguo lastReadPLC = DateTime.Now; return outVal; } /// /// Effettua lettura semafori principale Parametri da /// aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { DateTime adesso = DateTime.Now; lastReadPLC = adesso; // verifico non sia in veto invio iniziale... if (queueInEnabCurr) { try { if (verboseLog) { lgInfo("inizio read semafori"); } 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(); } } /// /// Override connessione /// 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 {cIobConf.cncPingAddr}"); } } } } else { needRefresh = true; } } public override void tryDisconnect() { // registro solo che è disconnesso connectionOk = false; queueInEnabCurr = false; } #endregion Public Methods #region Protected Fields /// /// Api Url di base x chiamate REST /// protected string apiUrl = "http://localhost:8733"; /// /// Num err connessioni corrente /// protected int connErrCur = 0; /// /// Max num err connesisone prima di disconnettere /// protected int connErrMax = 5; /// /// LookUpTable informazioni raccolte /// protected Dictionary RestDataLUT = new Dictionary(); /// /// Timeout chiamate REST /// protected int tOutSec = 60; #endregion Protected Fields #region Protected Properties /// /// Parametri specifici Client Rest /// protected RestParamConf restParams { get; set; } = new RestParamConf(); #endregion Protected Properties #region Protected Methods /// /// verifica stato ok ovvero connected oppure open /// /// /// protected bool checkStateOk(System.ServiceModel.CommunicationState currState) { bool answ = false; return answ; } /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO /// 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) ----------------------------------------------------- */ // per prima cosa controllo ping e se sia connesso... #if false 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}"); } } /// /// Esecuzione chiamata Rest tipo GET /// /// URI da chiamare /// Chiamata rapida (timeout rapido = 5sec) /// 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; } /// /// Esecuzione chiamata Rest tipo POST /// /// URI da chiamare /// Payload da allegare (già serializzato) /// Chiamata rapida (timeout rapido = 5sec) /// 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; } /// /// restituisce URL della risorsa eventualmente completato con token o altro /// /// /// 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; } /// /// Metodo da overridare x scrivere DAVVERO i parametri sul PLC /// /// protected override void plcWriteParams(ref List 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 } } /// /// Esegue lettura dati + salvataggio in LUT /// protected virtual void refreshData() { } /// /// Recupero valore da cache LUT /// /// /// protected string RestLutGet(string key) { string value = ""; if (RestDataLUT.ContainsKey(key)) { value = RestDataLUT[key]; } return value; } /// /// Upsert in cache LUT del parametro /// /// /// protected void RestLutUpsert(string key, string 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(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 /// /// Conf client RestSharp standard: /// - timeout 1 min /// private RestClientOptions restOptStd = new RestClientOptions { Timeout = TimeSpan.FromSeconds(60) }; #endregion Private Fields } }