925 lines
36 KiB
C#
925 lines
36 KiB
C#
using IOB_UT_NEXT;
|
|
using IOB_UT_NEXT.Config;
|
|
using IOB_UT_NEXT.Config.Special;
|
|
using IOB_UT_NEXT.Objects;
|
|
using IOB_UT_NEXT.Services.Files;
|
|
using MapoSDK;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Serialization;
|
|
using static IOB_UT_NEXT.CustomObj;
|
|
using static IOB_UT_NEXT.DataModel.UstdData;
|
|
|
|
namespace IOB_WIN_WS.IobWs
|
|
{
|
|
/// <summary>
|
|
/// Adapter specializzato per Taglierine EMMEGI (es Colcom) con le chiamate tramite RestSharp (XML/Soap) al FPWorkshop in ritorno+ invio tramite file CSV al sw Job
|
|
/// </summary>
|
|
public class EmmegiFPW : RestBase
|
|
{
|
|
#region Public Constructors
|
|
|
|
/// <summary>
|
|
/// Costruttore dell'IOB SOAP della bilancia EmmegiFPW
|
|
/// </summary>
|
|
/// <param name="caller">Form chiamante</param>
|
|
/// <param name="IobConfFull">Configurazione (v 4.x)</param>
|
|
public EmmegiFPW(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
|
|
{
|
|
lgInfo($"Richiesto Adapter IobRest.EmmegiFPW con i parametri seguenti | ADDR: {IOBConfFull.Device.Connect.IpAddr} | PORT: {IOBConfFull.Device.Connect.Port}");
|
|
DtHelp.lastPING = DateTime.Now.AddHours(-1);
|
|
redKeyAlarm = redisMan.redHash($"IOB:Status:{IOBConfFull.General.CodIOB}:Alarm:LastRead");
|
|
DtHelp.lastPING = DateTime.Now.AddHours(-1);
|
|
// predispongo configurazione specifica Rest...
|
|
lgInfo("01 - Rest");
|
|
if (!string.IsNullOrEmpty(getOptPar("REST_CONF")))
|
|
{
|
|
setupRestConf(getOptPar("REST_CONF"));
|
|
}
|
|
|
|
// sistemo chiavi specifiche redis...
|
|
rKeyConf = $"{redisMan.redIobKey}:Conf:ParamsUSTD";
|
|
rKeyMD5 = $"{redisMan.redIobKey}:FPW:FILE-MD5";
|
|
rKeyJob = $"{redisMan.redIobKey}:JOB";
|
|
lgInfo("02 - Redis");
|
|
|
|
// setup variabili custom... file cont in tools
|
|
string FileConfPath = IOBConfFull.OptParGet("FileImportConf");
|
|
if (!string.IsNullOrEmpty(FileConfPath))
|
|
{
|
|
// fix exclToolDirPath
|
|
exclToolDirPath = Path.Combine(FileMover.GetExecutingDirectoryName(), "Tools");
|
|
string fullPath = Path.Combine(exclToolDirPath, FileConfPath);
|
|
if (File.Exists(fullPath))
|
|
{
|
|
string rawData = File.ReadAllText(fullPath);
|
|
// carico conf...
|
|
ExtToolConf ConfigFile = JsonConvert.DeserializeObject<ExtToolConf>(rawData) ?? new ExtToolConf();
|
|
basePath = ConfigFile.FileOutPath;
|
|
archivePath = ConfigFile.ArchiveDir;
|
|
if (!Directory.Exists(archivePath))
|
|
{
|
|
Directory.CreateDirectory(archivePath);
|
|
}
|
|
|
|
lgInfo($"exclToolDirPath: {exclToolDirPath} | fullPath: {fullPath} | basePath: {basePath} | archivePath: {archivePath}");
|
|
lgInfo("03 - Conf");
|
|
}
|
|
}
|
|
|
|
// faccio un fileImport preliminare...
|
|
try
|
|
{
|
|
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
|
|
Task.Run(async () =>
|
|
{
|
|
await ProcessFileImportAsync();
|
|
})
|
|
.GetAwaiter()
|
|
.GetResult();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lgError($"Errore in ProcessFileImportAsync{Environment.NewLine}{ex.Message}");
|
|
}
|
|
|
|
if (EnableTest)
|
|
{
|
|
PrelimRestTest();
|
|
lgInfo("04 - Test");
|
|
}
|
|
}
|
|
|
|
#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:
|
|
* - 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 = SetJobDataEmmegiFPW();
|
|
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>();
|
|
return outVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupero dati DYN, impiegando i valori della LUT...
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override Dictionary<string, string> getDynData()
|
|
{
|
|
var answ = RestDataLUT;
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupera il progName in modalità custom...
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string getPrgName()
|
|
{
|
|
return RestLutGet("pName");
|
|
}
|
|
|
|
/// <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 rawData = ExecuteCallGet(GetUrlResource("GetStatus"), true);
|
|
lgInfo($"GetConnection | {rawData} | EmmegiFPW.tryConnect");
|
|
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
connectionOk = true;
|
|
queueInEnabCurr = true;
|
|
// salvo lo status con i relativi contenuti...
|
|
SaveCurrStatus(rawData);
|
|
if (adpRunning)
|
|
{
|
|
lgInfo("Connessione EmmegiFPW OK");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgError($"Errore check connessione EmmegiFPW | rawData: {rawData}");
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Public Classes
|
|
|
|
/// <summary>
|
|
/// Struttura delle risposte alle chiamare REST specifiche
|
|
/// </summary>
|
|
public class EmmegiResp
|
|
{
|
|
#region Public Enums
|
|
|
|
public enum MachineStatus
|
|
{
|
|
// manuale
|
|
ON,
|
|
|
|
// attrezzaggio = zero assi
|
|
SETUP,
|
|
|
|
// pronta, in automatico, in attesa carico pezzi
|
|
READY,
|
|
|
|
// posizionamento (teste morse o cambio utensili)
|
|
POSITIONING,
|
|
|
|
// attesa (carico/scarico barra, operazioni utente)
|
|
WAIT,
|
|
|
|
// in lavorazione effettiva
|
|
WORKING,
|
|
|
|
// emergenza
|
|
EMERGENCY,
|
|
|
|
// in allarme
|
|
ALARM,
|
|
|
|
// offline (poweroff?)
|
|
OFF_LINE,
|
|
|
|
OFF,
|
|
ERROR
|
|
}
|
|
|
|
#endregion Public Enums
|
|
|
|
#region Public Classes
|
|
|
|
[XmlRoot("result_workstation_status")]
|
|
public class StatusDTO
|
|
{
|
|
#region Public Properties
|
|
|
|
[XmlElement("workstation")]
|
|
public Workstation Workstation { get; set; }
|
|
|
|
#endregion Public Properties
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obj workstation
|
|
/// </summary>
|
|
public class Workstation
|
|
{
|
|
#region Public Properties
|
|
|
|
[XmlElement("id")]
|
|
public WorkstationId Id { get; set; }
|
|
|
|
[XmlElement("job_id")]
|
|
public string JobId { get; set; }
|
|
|
|
[XmlElement("operations")]
|
|
public int Operations { get; set; }
|
|
|
|
[XmlElement("operator")]
|
|
public string Operator { get; set; }
|
|
|
|
[XmlElement("status")]
|
|
public MachineStatus Status { get; set; }
|
|
|
|
[XmlIgnore]
|
|
public DateTime StatusTimestamp
|
|
{
|
|
get => DateTime.ParseExact(StatusTimestampRaw, "yyyy-MM-dd HH:mm:ss", null);
|
|
}
|
|
|
|
[XmlElement("status_timestamp")]
|
|
public string StatusTimestampRaw { get; set; }
|
|
|
|
[XmlElement("tot_operations")]
|
|
public int TotalOperations { get; set; }
|
|
|
|
#endregion Public Properties
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obj WorkstationId
|
|
/// </summary>
|
|
public class WorkstationId
|
|
{
|
|
#region Public Properties
|
|
|
|
[XmlAttribute("id")]
|
|
public string IdValue { get; set; }
|
|
|
|
#endregion Public Properties
|
|
}
|
|
|
|
#endregion Public Classes
|
|
|
|
#region Internal Classes
|
|
|
|
/// <summary>
|
|
/// Struttura info JobData da inviare x setup
|
|
/// </summary>
|
|
internal class EmmegiFPWJobData
|
|
{
|
|
#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
|
|
}
|
|
|
|
#endregion Internal Classes
|
|
}
|
|
|
|
#endregion Public Classes
|
|
|
|
#region Internal Methods
|
|
|
|
/// <summary>
|
|
/// Converte valore raw stato impianto nel formato specifico
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal EmmegiResp.StatusDTO GetStatus(string xmlRaw)
|
|
{
|
|
EmmegiResp.StatusDTO answ = new EmmegiResp.StatusDTO();
|
|
// deserializzo
|
|
var serializer = new XmlSerializer(typeof(EmmegiResp.StatusDTO));
|
|
using (var reader = new StringReader(xmlRaw))
|
|
{
|
|
answ = (EmmegiResp.StatusDTO)serializer.Deserialize(reader);
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#endregion Internal Methods
|
|
|
|
#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 status...
|
|
switch (currState)
|
|
{
|
|
case EmmegiResp.MachineStatus.ON:
|
|
case EmmegiResp.MachineStatus.SETUP:
|
|
case EmmegiResp.MachineStatus.WAIT:
|
|
B_input += (1 << 4);
|
|
break;
|
|
|
|
case EmmegiResp.MachineStatus.WORKING:
|
|
B_input += (1 << 1);
|
|
break;
|
|
|
|
case EmmegiResp.MachineStatus.EMERGENCY:
|
|
case EmmegiResp.MachineStatus.ALARM:
|
|
case EmmegiResp.MachineStatus.ERROR:
|
|
B_input += (1 << 3);
|
|
break;
|
|
|
|
case EmmegiResp.MachineStatus.OFF_LINE:
|
|
case EmmegiResp.MachineStatus.OFF:
|
|
B_input = 0;
|
|
break;
|
|
|
|
case EmmegiResp.MachineStatus.READY:
|
|
case EmmegiResp.MachineStatus.POSITIONING:
|
|
default:
|
|
B_input = 1;
|
|
break;
|
|
}
|
|
|
|
// accodo NON emergenza ... poi da cercare meglio...
|
|
B_input += (1 << 7);
|
|
}
|
|
else
|
|
{
|
|
B_input = 0;
|
|
currDispData.semIn = Semaforo.SR;
|
|
lgTrace($"EmmegiFPW.decodeToBaseBitmap | connectionOk: {connectionOk}");
|
|
}
|
|
}
|
|
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 = SetJobDataEmmegiFPW();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua eventuale file import del file excel come array json in REDIS
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override async Task<bool> ProcessFileImportAsync()
|
|
{
|
|
bool answ = false;
|
|
|
|
/* ------------------------------------------
|
|
* esecuzione cablata, si potrebbe costruire una cosa più flessibile tramite conf,
|
|
* la procedura prevede:
|
|
* - controllo MD5 file sorgente *.xlsx
|
|
* - verifica MD5 precedente
|
|
* - verifica in redis del dict seralizzato
|
|
*
|
|
* in caso di mancanza REDIS o variazione MD5 --> lancio script import dati col relativo file di conf
|
|
* ------------------------------------------*/
|
|
|
|
// init condizioni check
|
|
bool cachePresent = false;
|
|
bool changedFile = true;
|
|
string newMd5 = "";
|
|
string oldMd5 = "";
|
|
string rawData = "";
|
|
|
|
// cerco il file da controllare
|
|
string FileInPath = IOBConfFull.OptParGet("FileInPath");
|
|
if (!string.IsNullOrEmpty(FileInPath))
|
|
{
|
|
if (File.Exists(FileInPath))
|
|
{
|
|
// verifico presenza in cache REDIS
|
|
rawData = redisMan.getRSV(rKeyConf);
|
|
cachePresent = !string.IsNullOrEmpty(rawData) && rawData.Length > 2;
|
|
// calcolo MD5
|
|
newMd5 = baseUtils.GetFileMd5(FileInPath);
|
|
// verifico MD5 precedente
|
|
oldMd5 = redisMan.getRSV(rKeyMD5);
|
|
changedFile = string.IsNullOrEmpty(oldMd5) || !newMd5.Equals(oldMd5);
|
|
// se c'è una condizione di trigger effettuo import chiamando task esterno + conf file
|
|
if (changedFile || !cachePresent)
|
|
{
|
|
// recupero path file di conf x import
|
|
string FileConfPath = IOBConfFull.OptParGet("FileImportConf");
|
|
if (!string.IsNullOrEmpty(FileConfPath))
|
|
{
|
|
// avvio proc import tramite exe tool esterno...
|
|
FileProcMan fpm = new FileProcMan(exclToolDirPath, "ExcImport.exe", rKeyConf, FileConfPath);
|
|
lgInfo($"ProcessFileImportAsync | FileConfPath: {FileConfPath} | exclToolDirPath: {exclToolDirPath} | FileInPath: {FileInPath}");
|
|
fpm.doProcess(FileInPath, 0);
|
|
answ = true;
|
|
// aggiorno md5...
|
|
redisMan.setRSV(rKeyMD5, newMd5);
|
|
lgInfo($"Eseguito caricamento MD5 e DB conf ricette FPW");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// eseguo un SetJobDataEmmegiFPW ogni 60 sec...
|
|
if (DateTime.Now.Subtract(lastSetJob).TotalSeconds > 60)
|
|
{
|
|
await Task.Delay(1);
|
|
SetJobDataEmmegiFPW();
|
|
lastSetJob = DateTime.Now;
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <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;
|
|
// in primis testo che sia connessa...
|
|
var rawData = ExecuteCallGet(GetUrlResource("GetStatus"), true);
|
|
lgTrace($"refreshData.GetStatus | {rawData}");
|
|
|
|
// proseguo SOLO SE macchina OK, altrimenti disconnetto...
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
// salvo i dati status aggiornati
|
|
SaveCurrStatus(rawData);
|
|
lgInfo($"EmmegiFPW | refreshData | {currStatusDTO.Workstation.StatusTimestamp} | Status: {currStatusDTO.Workstation.Status} | TotCount {currStatusDTO.Workstation.TotalOperations}");
|
|
}
|
|
else
|
|
{
|
|
// verifico se devo disconnettere
|
|
if (connErrCur >= connErrMax)
|
|
{
|
|
connErrCur = 0;
|
|
tryDisconnect();
|
|
}
|
|
else
|
|
{
|
|
connErrCur++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Private Fields
|
|
|
|
/// <summary>
|
|
/// folder archivio (relativa a basePath)
|
|
/// </summary>
|
|
private string archivePath = "";
|
|
|
|
// path base scambio dati
|
|
private string basePath = "";
|
|
|
|
/// <summary>
|
|
/// Dizionario conf parametri taglio x taglierina Emmegi
|
|
/// </summary>
|
|
private Dictionary<string, CutterParam> DictParamsFPW = new Dictionary<string, CutterParam>();
|
|
|
|
private DateTime lastRestOtherCount = DateTime.Now;
|
|
private DateTime lastRestProcPzCount = DateTime.Now;
|
|
private DateTime lastRestRefreshData = DateTime.Now;
|
|
private DateTime lastSetJob = DateTime.Now.AddHours(-1);
|
|
|
|
/// <summary>
|
|
/// chiave salvataggio conf USTD
|
|
/// </summary>
|
|
private string rKeyConf = "";
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
private string rKeyJob = "";
|
|
|
|
/// <summary>
|
|
/// chiave check file config md5
|
|
/// </summary>
|
|
private string rKeyMD5 = "";
|
|
|
|
#endregion Private Fields
|
|
|
|
#region Private Properties
|
|
|
|
private EmmegiResp.MachineStatus currState
|
|
{
|
|
get => currStatusDTO.Workstation.Status;
|
|
}
|
|
|
|
private EmmegiResp.StatusDTO currStatusDTO { get; set; } = new EmmegiResp.StatusDTO();
|
|
|
|
/// <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 EmmegiFPW
|
|
/// </summary>
|
|
private void PrelimRestTest()
|
|
{
|
|
lgInfo("Rest - 03");
|
|
// test folder salvataggio log
|
|
var appPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
|
var fPath = Directory.GetParent(appPath).FullName;
|
|
if (!Directory.Exists(basePath))
|
|
{
|
|
Directory.CreateDirectory(basePath);
|
|
}
|
|
lgInfo("Rest - 04");
|
|
|
|
string rawData = TestRawCall("Get?qname=workstation_status¶m_1=C129696&complete=true", true);
|
|
lgInfo($"RawData:{Environment.NewLine}{rawData}");
|
|
// deserializzo
|
|
var cStatus = GetStatus(rawData);
|
|
string output = $"Dt: {cStatus.Workstation.StatusTimestamp} | WrkId: {cStatus.Workstation.Id.IdValue} | Operatore: {cStatus.Workstation.Operator} | Job: {cStatus.Workstation.JobId} | Status: {cStatus.Workstation.Status} | # Tagli Job: {cStatus.Workstation.Operations} | # Tot Tagli: {cStatus.Workstation.TotalOperations}";
|
|
|
|
lgInfo($"Status:{Environment.NewLine}{output}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua rilettura DB conf ricette FPW SEMPRE da REDIS
|
|
/// </summary>
|
|
private void ReloadConfDb()
|
|
{
|
|
// verifico presenza in cache REDIS
|
|
string rawData = redisMan.getRSV(rKeyConf);
|
|
if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2)
|
|
{
|
|
// deserializzo!
|
|
try
|
|
{
|
|
DictParamsFPW = JsonConvert.DeserializeObject<Dictionary<string, CutterParam>>(rawData);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in reloadConfDb{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Riceve un valore raw xml dello status e salva in LUT tutti i valori...
|
|
/// </summary>
|
|
/// <param name="rawData"></param>
|
|
private void SaveCurrStatus(string rawData)
|
|
{
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
try
|
|
{
|
|
// deserializzo
|
|
currStatusDTO = GetStatus(rawData);
|
|
RestLutUpsert("serial", currStatusDTO.Workstation.Id.IdValue);
|
|
RestLutUpsert("currPOdl", currStatusDTO.Workstation.JobId);
|
|
RestLutUpsert("operator", currStatusDTO.Workstation.Operator);
|
|
RestLutUpsert("status", currStatusDTO.Workstation.Status);
|
|
RestLutUpsert("currCount", currStatusDTO.Workstation.Operations);
|
|
RestLutUpsert("totCount", currStatusDTO.Workstation.TotalOperations);
|
|
RestLutUpsert("lastStatCh", currStatusDTO.Workstation.StatusTimestamp);
|
|
|
|
// verifico JobID x avvio/chiusura PODL + archiviazione files...
|
|
if (!string.IsNullOrEmpty(currStatusDTO.Workstation.JobId))
|
|
{
|
|
// verifico se cambiato...
|
|
string lastJobId = getCurrProdData("currJobId", "");
|
|
//var lastJobId = getCurrProdData("currPODL", "");
|
|
if (!lastJobId.Equals(currStatusDTO.Workstation.JobId))
|
|
{
|
|
startNewJob(lastJobId, currStatusDTO.Workstation.JobId);
|
|
// --> salvo come valore corrente...
|
|
upsertKey("currJobId", currStatusDTO.Workstation.JobId);
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua scrittura degli ODL aperti...
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool SetJobDataEmmegiFPW()
|
|
{
|
|
lgTrace("Inizio SetJobDataEmmegiFPW");
|
|
bool answ = false;
|
|
// conf path e file
|
|
//string basePath = @"x:\";
|
|
string srcPath = Path.Combine(exclToolDirPath, "TEMPLATE_ODL_USDT.csv");
|
|
|
|
// esegue rilettura DictDB parametri...
|
|
ReloadConfDb();
|
|
|
|
// ricerca file csv preesistenti, se fossero stati creati oltre xx Ore prima li considera scaduti
|
|
List<string> listFiles = Directory.GetFiles(basePath, "*.csv").ToList();
|
|
|
|
// recupero elenco PODL corrente...
|
|
List<PODLModel> reqPOdlList = MachineNextPodl();
|
|
|
|
string rawData = File.ReadAllText(srcPath);
|
|
|
|
foreach (var item in reqPOdlList)
|
|
{
|
|
// verifico che NON sia già presente... FARE controllo con età file (max 60 min?)
|
|
if (listFiles.Where(x => x.Contains(item.KeyBCode)).Count() > 0)
|
|
{
|
|
lgInfo($"Salto file preesistente: {item.KeyBCode}");
|
|
}
|
|
else
|
|
{
|
|
// scrivo x ogni PODL richiesto un file di processing...
|
|
string codPOdl = $"PODL{item.IdxPromessa:00000000}";
|
|
string codExt = item.KeyBCode;
|
|
// salvo associazione OPR/PODL (e viceversa) x impiego successivo
|
|
redisMan.setRSV($"{rKeyJob}:PODL:{codPOdl}", codExt);
|
|
redisMan.setRSV($"{rKeyJob}:OPR:{codExt}", codPOdl);
|
|
|
|
string fileName = Path.Combine(basePath, $"{codExt}.csv");
|
|
string fileCont = rawData;
|
|
// cerco item nel DB...
|
|
if (DictParamsFPW.ContainsKey(item.CodArticolo))
|
|
{
|
|
var art = DictParamsFPW[item.CodArticolo];
|
|
if (art != null)
|
|
{
|
|
// effettuo sostituzioni...
|
|
fileCont = fileCont.Replace("[[ODL]]", codPOdl);
|
|
fileCont = fileCont.Replace("[[SERIE]]", art.Serie);
|
|
fileCont = fileCont.Replace("[[CODICE]]", art.Codice);
|
|
fileCont = fileCont.Replace("[[ALTEZZA]]", $"{art.Altezza.ToString("N1", NumberFormatInfo.InvariantInfo)}");
|
|
fileCont = fileCont.Replace("[[QUOTA_USCITA]]", $"{art.QuotaUscita.ToString("N1", NumberFormatInfo.InvariantInfo)}");
|
|
fileCont = fileCont.Replace("[[CODICE_LUNGH]]", $"{art.Codice}_{(art.LungPezzo * 10).ToString("000", NumberFormatInfo.InvariantInfo)}");
|
|
fileCont = fileCont.Replace("[[QTA]]", $"{item.NumPezzi}");
|
|
fileCont = fileCont.Replace("[[LUNGH]]", $"{art.LungPezzo.ToString("N1", NumberFormatInfo.InvariantInfo)}");
|
|
fileCont = fileCont.Replace("[[CLIENTE]]", "COLCOM");
|
|
|
|
// scrivo file...
|
|
File.WriteAllText(fileName, fileCont);
|
|
|
|
lgTrace($"Generato il file di lavoro: {codExt}.csv per PODL {codPOdl} |fname: {fileName}");
|
|
}
|
|
}
|
|
}
|
|
// segno fatto
|
|
answ = true;
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue setup nuovo Job:
|
|
/// - chiusura ODL precedente
|
|
/// - avvio PODL
|
|
/// - archiviazione file OPR*.csv
|
|
/// - TTL cache redis in disabilitazione
|
|
/// </summary>
|
|
/// <param name="jobIdOld">Vecchio JobId</param>
|
|
/// <param name="jobIdNew">Nuovo JobId</param>
|
|
private void startNewJob(string jobIdOld, string jobIdNew)
|
|
{
|
|
// JobId = OPRxx-yyyyy
|
|
lgInfo($"Richiesta startNewJob: {jobIdOld} --> {jobIdNew}");
|
|
|
|
// recupero PODL da chiudere da OPR...
|
|
string codPODL = redisMan.getRSV($"{rKeyJob}:OPR:{jobIdOld}");
|
|
int idxPODL = 0;
|
|
int.TryParse(codPODL.Replace("PODL", ""), out idxPODL);
|
|
//... e lo chiudo
|
|
if (idxPODL > 0)
|
|
{
|
|
// chiudo
|
|
SendClosePOdl(idxPODL, DateTime.Now);
|
|
// svuoto cache REDIS con adeguato TTL a 3 gg...
|
|
redisMan.setRSV($"{rKeyJob}:PODL:{codPODL}", jobIdOld, 3600 * 24 * 3);
|
|
redisMan.setRSV($"{rKeyJob}:OPR:{jobIdOld}", codPODL, 3600 * 24 * 3);
|
|
}
|
|
|
|
// recupero dati nuovo PODL
|
|
string newPODL = redisMan.getRSV($"{rKeyJob}:OPR:{jobIdNew}");
|
|
int idxPODLNew = 0;
|
|
int.TryParse(newPODL.Replace("PODL", ""), out idxPODLNew);
|
|
//... e lo chiudo
|
|
if (idxPODLNew > 0)
|
|
{
|
|
// avvio PODL nuovo chiudendo eventuali vecchio ODL
|
|
SendStartPodl(idxPODLNew, DateTime.Now);
|
|
// svuoto cache REDIS con adeguato TTL a 3 gg...
|
|
redisMan.setRSV($"{rKeyJob}:PODL:{newPODL}", jobIdNew, 3600 * 24 * 3);
|
|
redisMan.setRSV($"{rKeyJob}:OPR:{jobIdNew}", newPODL, 3600 * 24 * 3);
|
|
}
|
|
|
|
// archivio file csv x anno...
|
|
string filePath = Path.Combine(basePath, $"{jobIdOld}.csv");
|
|
if (File.Exists(filePath))
|
|
{
|
|
// verifico archive dir dell'anno/mese corrente...
|
|
DateTime oggi = DateTime.Today;
|
|
string monthDir = Path.Combine(archivePath, $"{oggi.Year:yyyy}", $"{oggi.Month:MM}");
|
|
if (!Directory.Exists(monthDir))
|
|
{
|
|
Directory.CreateDirectory(monthDir);
|
|
}
|
|
// sposto!
|
|
string filePathDest = Path.Combine(monthDir, $"{jobIdOld}.csv");
|
|
File.Move(filePath, filePathDest);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test Chiamata EmmegiFPW
|
|
/// </summary>
|
|
/// <param name="urlReq"></param>
|
|
/// <returns></returns>
|
|
private string TestRawCall(string urlReq, bool fastCall = false)
|
|
{
|
|
lgInfo($"TestRawCall: {urlReq}");
|
|
string answ = ExecuteCallGet(urlReq, fastCall);
|
|
lgInfo($"TestRawCall resp: {answ}");
|
|
return answ;
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
} |