Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobRest/Citizen.cs
T
2024-11-29 15:24:01 +01:00

732 lines
29 KiB
C#

using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Mapping;
using System.IO;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
using static IOB_WIN_NEXT.IobRest.Citizen.Responses;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace IOB_WIN_NEXT.IobRest
{
/// <summary>
/// Adapter specializzato per Citizen (Colcom, MT70)
/// </summary>
public class Citizen : Base
{
#region Public Constructors
/// <summary>
/// Costruttore dell'IOB SOAP della bilancia RestCitizen
/// </summary>
/// <param name="caller">AdapterForm chiamante</param>
/// <param name="IOBConf">Configurazione IOB per avvio</param>
public Citizen(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo($"Richiesto Adapter IobRest.RestCitizen con i parametri seguenti | ADDR: {IOBConf.cncIpAddr} | PORT: {IOBConf.cncPort}");
lastPING = DateTime.Now.AddHours(-1);
redKeyAlarm = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:Alarm:LastRead");
lastPING = DateTime.Now.AddHours(-1);
// predispongo configurazione specifica Rest...
lgInfo("Rest - 01");
if (!string.IsNullOrEmpty(getOptPar("REST_CONF")))
{
setupRestConf(getOptPar("REST_CONF"));
}
// sistemo conf errori e samplePeriod...
lgInfo("Rest - 02");
// fixme todo levare forzatura test iniziale...
if (EnableTest)
{
PrelimCitizenRestTest();
}
}
#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:
* - setArt
* - setComm
* - setPzComm
*---------------------------------------*/
// 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.setArt:
case taskType.setComm:
case taskType.setPzComm:
lgInfo($"Task: {item.Key} | {item.Value}");
// salvo valori in memoria prod data
upsertKey(item.Key, item.Value);
taskVal = $"SET task: {item.Key} --> {item.Value}";
// invio una chiamata al sistema con i valori attuali
taskOk = SetJobDataCitizen();
break;
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>
/// Recupera dizionario allarmi (SE PRESENTE)
/// </summary>
/// <returns></returns>
public override Dictionary<string, string> getAlarmData()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
// verifico SE HO allarmi attivi e nel caso li invia...
string rawData = ExecuteCallGet(GetUrlResource("GetCurrAlarm"));
string bankVal = "OK";
WarnInfo alarmResp = JsonConvert.DeserializeObject<WarnInfo>(rawData);
bankVal = (alarmResp != null && alarmResp.IsAlarm) ? alarmResp.message : "OK";
saveAlarmString(ref outVal, bankVal, "Alarms");
// verificare eventuale invio allarmi "storici"
return outVal;
}
/// <summary>
/// Recupero dati DYN, impiegando i valori della LUT...
/// </summary>
/// <returns></returns>
public override Dictionary<string, string> getDynData()
{
var answ = RestDataLUT;
// aggiungo DynData... calcolato come variazione pezzi e lampade...
if (answ.ContainsKey("DYNDATA"))
{
answ.Remove("DYNDATA");
}
answ.Add("DYNDATA", $"LAMP_{RestLutGet("gLamp")}{RestLutGet("yLamp")}{RestLutGet("rLamp")}_PzReq_{RestLutGet("qtyReq")}_PzWrk_{RestLutGet("qtyWrk")}_PzTot_{RestLutGet("qtyTot")}");
return answ;
}
/// <summary>
/// Recupera il progName in modalità custom...
/// </summary>
/// <returns></returns>
public override string getPrgName()
{
return RestLutGet("pName");
}
/// <summary>
/// Esegue gestioen contapezzi...
/// </summary>
public override void processContapezzi()
{
DateTime adesso = DateTime.Now;
// controllo se sia passato il samplePeriod minimo x 3...
if (adesso.Subtract(lastRestProcPzCount.AddMilliseconds(samplePeriod * 3)).TotalSeconds > 0)
{
lastRestProcPzCount = adesso;
bool checkMachine = false;
LadderIO ladderResp = new LadderIO();
// in primis testo che sia connessa...
var checkResp = ExecuteCallGet(GetUrlResource("GetConnection"), true);
lgInfo($"GetConnection | {checkResp} | Citizen.processContapezzi");
bool.TryParse(checkResp, out checkMachine);
// proseguo SOLO SE macchina OK, altrimenti disconnetto...
if (checkMachine)
{
// faccio refresh token comunque...
string token = ExecuteCallGet(GetUrlResource("GetToken"));
token = token.Replace("\"", "");
RestLutUpsert("token", token);
// contapezzi corrente
int intVal = 0;
string qtyWrk = ExecuteCallGet(GetUrlResource("GetQtyProd"));
int.TryParse(qtyWrk, out intVal);
contapezziPLC = intVal;
RestLutUpsert("qtyWrk", qtyWrk);
// gestione altri contapezzi x
string qtyTot = ExecuteCallGet(GetUrlResource("GetQtyTot"));
string qtyReq = ExecuteCallGet(GetUrlResource("GetQtyReq"));
RestLutUpsert("qtyTot", qtyTot);
RestLutUpsert("qtyReq", qtyReq);
// gestione tempociclo e nome programma...
string cTime = ExecuteCallGet(GetUrlResource("GetCycleTime"));
string pName = ExecuteCallGet(GetUrlResource("GetProgName"));
RestLutUpsert("cTime", cTime);
RestLutUpsert("pName", pName);
lgInfo($"Citizen | contapezziPLC {qtyWrk} | qtyReq: {qtyReq} | qtyTot: {qtyTot}");
}
}
}
/// <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;
bool checkMachine = false;
// 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);
lgInfo($"GetConnection | {checkResp} | Citizen.tryConnect");
bool.TryParse(checkResp, out checkMachine);
if (checkMachine)
{
connectionOk = true;
queueInEnabCurr = true;
if (adpRunning)
{
lgInfo("Connessione Citizen OK");
}
}
else
{
lgError($"Errore check connessione Citizen | checkResp: {checkResp}");
}
}
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;
}
}
#endregion Public Methods
#region Internal Classes
/// <summary>
/// Struttura delle risposte alle chiamare REST specifiche
/// </summary>
internal class Responses
{
#region Internal Classes
/// <summary>
/// Struttura info restituiti da AlarmList
/// </summary>
internal class AlarmInfo
{
#region Public Properties
public DateTime date { get; set; } = DateTime.Today.AddYears(-10);
public string ErrorMessage { get; set; } = "";
public int id { get; set; } = 0;
public string message { get; set; } = "";
public string type { get; set; } = "";
#endregion Public Properties
}
/// <summary>
/// Struttura info JobData da inviare x setup
/// </summary>
internal class CitizenJobData
{
#region Public Properties
public string itemcode { get; set; } = "";
public string itemdescription { get; set; } = "";
public string ordercode { get; set; } = "";
public int piecestodo { get; set; } = 0;
public string token { get; set; } = "";
#endregion Public Properties
}
/// <summary>
/// Struttura info restituiti x lettura Ladder
/// </summary>
internal class LadderIO
{
#region Public Properties
public string Address { get; set; } = "";
public string Description { get; set; } = "";
public string ErrorMessage { get; set; } = "";
public string IOType { get; set; } = "";
public string VarValue { get; set; } = "";
#endregion Public Properties
}
/// <summary>
/// Struttura info QtyData da inviare x impostazioni quantità
/// </summary>
internal class QtyData
{
#region Public Properties
public string token { get; set; } = "";
public int value { get; set; } = 0;
#endregion Public Properties
}
/// <summary>
/// Struttura dato AlarmState (singolo, corrente)
/// </summary>
internal class WarnInfo
{
#region Public Properties
public string ErrorMessage { get; set; } = "";
public bool IsAlarm { get; set; } = false;
public bool IsCaution { get; set; } = false;
public string message { get; set; } = "";
#endregion Public Properties
}
/// <summary>
/// Struttura risposta x scrittura parametri numerici (es qty)
/// </summary>
internal class WriteResult
{
#region Public Properties
public string ErrorMessage { get; set; } = null;
public bool Result { get; set; } = false;
#endregion Public Properties
}
#endregion Internal Classes
}
#endregion Internal Classes
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override 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 (connectionOk)
{
B_input = 1;
currDispData.semIn = Semaforo.SV;
// controllo lampade... da rosso a verde...
if (RestLutGet("rLamp") == "1")
{
B_input += (1 << 3);
}
else if (RestLutGet("yLamp") == "1")
{
B_input += (1 << 4);
}
else if (RestLutGet("gLamp") == "1")
{
B_input += (1 << 1);
}
// accodo NON emergenza ... poi da cercare meglio...
B_input += (1 << 7);
}
else
{
B_input = 0;
currDispData.semIn = Semaforo.SR;
}
}
else
{
lgDebug($"[VETO queueInEnabCurr] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}");
}
}
/// <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 ricevuti
upsertKey(item.uid, item.reqValue);
// resetto richiesta!
item.reqValue = "";
// chiamo scrittura commessa!
var taskOk = SetJobDataCitizen();
}
}
/// <summary>
/// Esegue lettura dati + salvataggio in LUT
/// </summary>
protected override void refreshData()
{
DateTime adesso = DateTime.Now;
// controllo se sia passato il samplePeriod minimo...
if (adesso.Subtract(lastRestRefreshData.AddMilliseconds(samplePeriod)).TotalSeconds > 0)
{
lastRestRefreshData = adesso;
bool checkMachine = false;
LadderIO ladderResp = new LadderIO();
// in primis testo che sia connessa...
var checkResp = ExecuteCallGet(GetUrlResource("GetConnection"), true);
lgInfo($"GetConnection | {checkResp} | Citizen.refreshData");
bool.TryParse(checkResp, out checkMachine);
// proseguo SOLO SE macchina OK, altrimenti disconnetto...
if (checkMachine)
{
//riporto err conn a zero
connErrCur = 0;
// faccio refresh token comunque...
string token = ExecuteCallGet(GetUrlResource("GetToken"));
token = token.Replace("\"", "");
RestLutUpsert("token", token);
// ora controllo valori lampada SE scaduti resetto 3 valori lamp
RestLutUpsert("gLamp", "0");
RestLutUpsert("yLamp", "0");
RestLutUpsert("rLamp", "0");
int signVal = 0;
string gLamp = ExecuteCallGet(GetUrlResource("GetLampGreen"));
ladderResp = JsonConvert.DeserializeObject<LadderIO>(gLamp);
if (string.IsNullOrEmpty(ladderResp.ErrorMessage))
{
int.TryParse(ladderResp.VarValue, out signVal);
RestLutUpsert("gLamp", $"{signVal}");
}
// se il valore è zero --> proseguo con yellow!
if (signVal == 0)
{
string yLamp = ExecuteCallGet(GetUrlResource("GetLampYellow"));
ladderResp = JsonConvert.DeserializeObject<LadderIO>(yLamp);
if (string.IsNullOrEmpty(ladderResp.ErrorMessage))
{
int.TryParse(ladderResp.VarValue, out signVal);
RestLutUpsert("yLamp", $"{signVal}");
}
}
// se il valore è zero --> proseguo con red!
if (signVal == 0)
{
string rLamp = ExecuteCallGet(GetUrlResource("GetLampRed"));
ladderResp = JsonConvert.DeserializeObject<LadderIO>(rLamp);
if (string.IsNullOrEmpty(ladderResp.ErrorMessage))
{
int.TryParse(ladderResp.VarValue, out signVal);
RestLutUpsert("rLamp", $"{signVal}");
}
}
}
else
{
// verifico se devo disconnettere
if (connErrCur >= connErrMax)
{
connErrCur = 0;
tryDisconnect();
}
else
{
connErrCur++;
}
}
}
}
#endregion Protected Methods
#region Private Fields
private string basePath = "";
private DateTime lastRestProcPzCount = DateTime.Now;
private DateTime lastRestRefreshData = DateTime.Now;
#endregion Private Fields
#region Private Properties
/// <summary>
/// Elenco pesate attuali
/// </summary>
private List<AlarmInfo> listAllarmiCurr { get; set; } = new List<AlarmInfo>();
/// <summary>
/// CHiave redis di salvataggio ultimo set di allarmi scaricato
/// </summary>
private string redKeyAlarm { get; set; } = "";
#endregion Private Properties
#region Private Methods
/// <summary>
/// Test preliminare comunicazione Rest Citizen
/// </summary>
private void PrelimCitizenRestTest()
{
lgInfo("Rest - 03");
// test folder salvataggio log
var appPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
var fPath = Directory.GetParent(appPath).FullName;
basePath = Path.Combine(fPath, "Test");
if (!Directory.Exists(basePath))
{
Directory.CreateDirectory(basePath);
}
lgInfo("Rest - 04");
bool checkMachine = false;
string checkResp = TestCitizenCall("checkconnection", true);
lgInfo($"checkconnection | {checkResp}");
bool.TryParse(checkResp, out checkMachine);
if (checkMachine)
{
string token = TestCitizenCall("gettoken/mecmaticames/mecmatica@mes");
token = token.Replace("\"", "");
if (string.IsNullOrEmpty(token))
{
lgError("Errore: token vuoto");
}
else
{
string gLamp = TestCitizenCall($"getsingleladderio/{token}/Y/8E");
string yLamp = TestCitizenCall($"getsingleladderio/{token}/Y/21");
string rLamp = TestCitizenCall($"getsingleladderio/{token}/Y/22");
string qtyTot = TestCitizenCall($"gettotalquantity/{token}");
string qtyReq = TestCitizenCall($"getneededquantity/{token}");
string qtyWrk = TestCitizenCall($"getworkedquantity/{token}");
string cTime = TestCitizenCall($"getcycletime/{token}");
string pName = TestCitizenCall($"getmainprogram/{token}");
string cAlarm = ExecuteCallGet($"getalarm/{token}");
var sAlarm = JsonConvert.DeserializeObject<WarnInfo>(cAlarm);
cAlarm = JsonConvert.SerializeObject(sAlarm, Formatting.Indented);
lgInfo($"Call: getalarm/{token} | resp: {cAlarm}");
string allAlarm = ExecuteCallGet($"getallalarmlog/{token}");
var alarmList = JsonConvert.DeserializeObject<List<AlarmInfo>>(allAlarm);
allAlarm = JsonConvert.SerializeObject(alarmList, Formatting.Indented);
lgInfo($"Call: getallalarmlog/{token} | resp: {allAlarm}");
string err = TestCitizenCall($"getsingleladderio/ABC/Y/22");
// salvo i valori
File.WriteAllText(Path.Combine(basePath, "token.txt"), token);
File.WriteAllText(Path.Combine(basePath, "gLamp.txt"), gLamp);
File.WriteAllText(Path.Combine(basePath, "yLamp.txt"), yLamp);
File.WriteAllText(Path.Combine(basePath, "rLamp.txt"), rLamp);
File.WriteAllText(Path.Combine(basePath, "qtyTot.txt"), qtyTot);
File.WriteAllText(Path.Combine(basePath, "qtyReq.txt"), qtyReq);
File.WriteAllText(Path.Combine(basePath, "qtyWrk.txt"), qtyWrk);
File.WriteAllText(Path.Combine(basePath, "cTime.txt"), cTime);
File.WriteAllText(Path.Combine(basePath, "pName.txt"), pName);
File.WriteAllText(Path.Combine(basePath, "cAlarm.txt"), cAlarm);
File.WriteAllText(Path.Combine(basePath, "allAlarm.txt"), allAlarm);
File.WriteAllText(Path.Combine(basePath, "err.txt"), err);
}
}
}
/// <summary>
/// Effettua scrittura set di parametri del job e restitusice task eseguito...
/// </summary>
/// <returns></returns>
private bool SetJobDataCitizen()
{
bool answ = false;
string varValue = "";
CitizenJobData jobReq = new CitizenJobData();
QtyData qtyReq = new QtyData();
// Token!
varValue = RestLutGet("token");
jobReq.token = varValue;
qtyReq.token = varValue;
// Commessa
varValue = getCurrProdData("setComm", "");
jobReq.ordercode = varValue;
// Articolo
varValue = getCurrProdData("setArt", "");
jobReq.itemcode = varValue;
jobReq.itemdescription = $"ART: {varValue}";
// Qty
varValue = getCurrProdData("setPzComm", "");
int pzReq = 0;
int.TryParse(varValue, out pzReq);
jobReq.piecestodo = pzReq;
qtyReq.value = pzReq;
try
{
// preparo payload
string jobPayload = JsonConvert.SerializeObject(jobReq);
// faccio chiamata POST x invio
var rawResp = ExecuteCallPost(GetUrlResource("SetJobData"), jobPayload);
bool.TryParse(rawResp, out answ);
// se andatae se ho la chaive x invio abilitata invio anche la qty richiesta...
if (answ)
{
string urlNeedQty = GetUrlResource("SetNeededQuantity");
if (string.IsNullOrEmpty(urlNeedQty))
{
lgInfo($"Set NeedQty NON abilitato, non invio");
}
else
{
jobPayload = JsonConvert.SerializeObject(qtyReq);
var rawRespQty = ExecuteCallPost(GetUrlResource("SetNeededQuantity"), jobPayload);
}
}
}
catch (Exception exc)
{
lgError($"Eccezione in SetJobDataCitizen{Environment.NewLine}{exc}");
}
return answ;
}
/// <summary>
/// Test Chiamata Citizen
/// </summary>
/// <param name="urlReq"></param>
/// <returns></returns>
private string TestCitizenCall(string urlReq, bool fastCall = false)
{
lgInfo($"TestCitizenCall: {urlReq}");
string answ = ExecuteCallGet(urlReq, fastCall);
lgInfo($"TestCitizenCall resp: {answ}");
return answ;
}
#endregion Private Methods
}
}