571 lines
22 KiB
C#
571 lines
22 KiB
C#
using EgwProxy.Ftp;
|
|
using IOB_UT_NEXT;
|
|
using MapoSDK;
|
|
using Newtonsoft.Json;
|
|
using Opc.Ua;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using static IOB_UT_NEXT.CustomObj;
|
|
|
|
namespace IOB_WIN_NEXT
|
|
{
|
|
public class IobOpcUaMBHCimolai : IobOpcUaMBH
|
|
{
|
|
#region Public Constructors
|
|
|
|
/// <summary>
|
|
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
|
|
/// gestione specifica per MBH (es Cimolai, Baglietto) https://github.com/OPCFoundation/UA-.NETStandard
|
|
/// </summary>
|
|
/// <param name="caller"></param>
|
|
/// <param name="IOBConf"></param>
|
|
public IobOpcUaMBHCimolai(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
|
|
{
|
|
lgInfo("Init OpcUa MBH versione Cimolai (Baglietto)");
|
|
|
|
// inizializzo classe base...
|
|
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
|
|
{
|
|
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
|
|
}
|
|
sendKeyRichiesta = true;
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Processo i task richiesti e li elimino dalla coda 2:2
|
|
/// </summary>
|
|
/// <param name="task2exe"></param>
|
|
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
|
|
{
|
|
// uso metodo base x salvare esito scrittura
|
|
var writeResult = base.executeTasks(task2exe);
|
|
|
|
// aggiungo comportamento custom: se ho impostato nome ricetta (programma) --> imposto
|
|
// richiesta caricamento se ho richiesto reset o fine lavoro --> imposto azzeramento
|
|
// esco restituendo risutlato scrittura iniziali
|
|
if (task2exe != null)
|
|
{
|
|
// controllo se memMap != null...
|
|
if (memMap != null)
|
|
{
|
|
bool taskOk = false;
|
|
string taskVal = "";
|
|
// cerco task specifici x OMP
|
|
foreach (var item in task2exe)
|
|
{
|
|
taskOk = false;
|
|
taskVal = "";
|
|
// converto richiesta in enum...
|
|
taskType tName = taskType.nihil;
|
|
Enum.TryParse(item.Key, out tName);
|
|
// controllo sulla KEY...
|
|
switch (tName)
|
|
{
|
|
case taskType.setProg:
|
|
// recupero dati da memMap...
|
|
if (memMap != null && memMap.mMapWrite != null)
|
|
{
|
|
if (memMap.mMapWrite.ContainsKey(item.Key))
|
|
{
|
|
dataConf currMem = memMap.mMapWrite[item.Key];
|
|
string addr = currMem.memAddr;
|
|
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
|
|
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
|
memMap.mMapWrite[item.Key].value = item.Value;
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
|
|
}
|
|
// salvo in currProd..
|
|
saveProdData(new KeyValuePair<string, string>(item.Key, item.Value));
|
|
|
|
break;
|
|
|
|
case taskType.startSetup:
|
|
setFineLotto();
|
|
break;
|
|
|
|
case taskType.stopSetup:
|
|
setInizioProd();
|
|
break;
|
|
|
|
case taskType.syncDbData:
|
|
iobGetDataFromServer();
|
|
iobWriteLocalCSV();
|
|
iobSendFTP("");
|
|
break;
|
|
|
|
default:
|
|
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
|
|
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
|
|
}
|
|
}
|
|
|
|
return writeResult;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua vero processing contapezzi
|
|
/// </summary>
|
|
public override void processContapezzi()
|
|
{
|
|
// non fa nulal (non ha contapezzi)
|
|
}
|
|
|
|
public override bool resetcontapezziPLC()
|
|
{
|
|
bool answ = false;
|
|
try
|
|
{
|
|
List<WriteValue> nodes2Write = new List<WriteValue>();
|
|
foreach (var item in opcUaParams.actResetCounter)
|
|
{
|
|
WriteValue commWriteVal = new WriteValue();
|
|
commWriteVal.NodeId = new NodeId(item.Key);
|
|
commWriteVal.AttributeId = Attributes.Value;
|
|
commWriteVal.Value = new DataValue();
|
|
commWriteVal.Value.Value = item.Value;
|
|
|
|
nodes2Write.Add(commWriteVal);
|
|
}
|
|
// vera scrittura
|
|
UA_ref.WriteNodes(nodes2Write);
|
|
answ = true;
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Internal Methods
|
|
|
|
internal override void procRunMode(ref string currRun)
|
|
{
|
|
// variabili RUN... se richiesto invio runMode
|
|
if (opcUaParams.runModeSend)
|
|
{
|
|
if (!string.IsNullOrEmpty(opcUaParams.keyRunMode))
|
|
{
|
|
currRun = getDataItemValue(opcUaParams.keyRunMode);
|
|
|
|
|
|
/* gestione specifica:
|
|
*
|
|
* - verifica valore InCorso x lavora/non lavora
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
if (!string.IsNullOrEmpty(currRun))
|
|
{
|
|
// se ho valore --> invio
|
|
string sVal = "";
|
|
string descr = $"RunModeVal";
|
|
DateTime locTStamp = DateTime.Now;
|
|
sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {opcUaParams.keyRunMode} | Val: {currRun}";
|
|
accodaFLog(sVal, qEncodeFLog(descr, currRun));
|
|
|
|
// se richiesto provo a tradurre
|
|
if (opcUaParams.runModeTrad)
|
|
{
|
|
string RunModeDescr = itemTranslation("RunMode", currRun);
|
|
descr = $"RunMode";
|
|
accodaFLog(sVal, qEncodeFLog(descr, RunModeDescr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Internal Methods
|
|
|
|
#region Protected Class
|
|
|
|
// <Auto-Generated>
|
|
// This is here so CodeMaid doesn't reorganize this document
|
|
// </Auto-Generated>
|
|
protected class ArtRow
|
|
{
|
|
#region Public Properties
|
|
|
|
[Description("Matricola_articolo")]
|
|
public string Matricola { get; set; } = "";
|
|
|
|
[Description("Articolo")]
|
|
//[Display(Name = "Articolo", Order = 1)]
|
|
public string Articolo { get; set; } = "";
|
|
|
|
[Description("Identificazione")]
|
|
//[Display(Name = "Identificazione", Order = 2)]
|
|
public string Descrizione { get; set; } = "";
|
|
|
|
|
|
[Description("Peso_teorico_linea1[ton]")]
|
|
public int Peso_01 { get; set; } = 0;
|
|
|
|
[Description("Peso_teorico_linea2[ton]")]
|
|
public int Peso_02 { get; set; } = 0;
|
|
|
|
[Description("Peso_teorico_linea3[ton]")]
|
|
public int Peso_03 { get; set; } = 0;
|
|
|
|
[Description("Peso_teorico_linea4[ton]")]
|
|
public int Peso_04 { get; set; } = 0;
|
|
|
|
[Description("Pos. Carrello 1[mm]")]
|
|
public int PosizCarrello_01 { get; set; } = 0;
|
|
|
|
[Description("Pos. Carrello 2[mm]")]
|
|
public int PosizCarrello_02 { get; set; } = 0;
|
|
|
|
[Description("Pos. Carrello 3[mm]")]
|
|
public int PosizCarrello_03 { get; set; } = 0;
|
|
|
|
[Description("Pos. Carrello 4[mm]")]
|
|
public int PosizCarrello_04 { get; set; } = 0;
|
|
|
|
[Description("Limitazione_velocita_sollevamento[%]")]
|
|
//[Display(Name = "Limitazione_velocita_sollevamento[%]", Order = 3)]
|
|
public int LimiteVel { get; set; } = 100;
|
|
|
|
#endregion Public Properties
|
|
}
|
|
// <Auto-Generated>
|
|
// This is here so CodeMaid doesn't reorganize this document
|
|
// </Auto-Generated>
|
|
protected class JobRow
|
|
{
|
|
#region Public Properties
|
|
|
|
[Description("Matricola")]
|
|
public string Matricola { get; set; } = "";
|
|
|
|
[Description("Commessa")]
|
|
public string Commessa { get; set; } = "";
|
|
|
|
[Description("Articolo")]
|
|
public string Articolo { get; set; } = "";
|
|
|
|
[Description("Identificazione")]
|
|
public string Descrizione { get; set; } = "";
|
|
|
|
[Description("Data inserimento")]
|
|
public string DataIns { get; set; } = "";
|
|
|
|
[Description("Ora inserimento")]
|
|
public string OraIns { get; set; } = "";
|
|
|
|
[Description("Lavorazione")]
|
|
public string Lavorazione { get; set; } = "";
|
|
|
|
#endregion Public Properties
|
|
}
|
|
|
|
#endregion Protected Class
|
|
|
|
#region Protected Properties
|
|
|
|
protected List<ArtRow> ListaArticoli { get; set; } = new List<ArtRow>();
|
|
|
|
protected List<JobRow> ListaJobs { get; set; } = new List<JobRow>();
|
|
|
|
#endregion Protected Properties
|
|
|
|
#region Protected Methods
|
|
|
|
/// <summary>
|
|
/// Recupera da server set di dati specifici x IOB : qui per compilare files CSV
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override bool iobGetDataFromServer()
|
|
{
|
|
bool answ = false;
|
|
bool okArt = false;
|
|
bool okDoss = false;
|
|
bool okPodl = false;
|
|
bool okLVFasi = false;
|
|
List<PODLModel> listaPODL = new List<PODLModel>();
|
|
List<DossiersModel> listaDoss = new List<DossiersModel>();
|
|
List<AnagArticoli> listaArt = new List<AnagArticoli>();
|
|
List<ListVal> anagLVFasi = new List<ListVal>();
|
|
Dictionary<string, string> dictAF = new Dictionary<string, string>();
|
|
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
|
|
var rawListArt = callUrl(urlGetCurrArt, false);
|
|
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
|
|
var rawListPODL = callUrl(urlGetCurrPODL, false);
|
|
var rawLVFasi = callUrl(urlGetListValFasiPodl, false);
|
|
if (!string.IsNullOrEmpty(rawListArt))
|
|
{
|
|
try
|
|
{
|
|
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
|
|
okArt = listaArt.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawListDOSS))
|
|
{
|
|
try
|
|
{
|
|
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
|
|
okDoss = listaDoss.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawListPODL))
|
|
{
|
|
try
|
|
{
|
|
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
|
|
okPodl = listaPODL.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawLVFasi))
|
|
{
|
|
try
|
|
{
|
|
anagLVFasi = JsonConvert.DeserializeObject<List<ListVal>>(rawLVFasi);
|
|
dictAF = anagLVFasi.ToDictionary(x => x.value, x => x.label);
|
|
okLVFasi = listaArt.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ListVal ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ListVal ({urlGetListValFasiPodl}) ha restituito valore vuoto");
|
|
}
|
|
answ = okPodl && okDoss && okArt && okLVFasi;
|
|
if (answ)
|
|
{
|
|
// predispongo dati PODL
|
|
ListaJobs = listaPODL
|
|
.Select(x => new JobRow() { Matricola = x.CodArticolo, Commessa = $"PODL{x.IdxPromessa:00000000}", Articolo = x.CodArticolo, Descrizione = x.CodArticolo, DataIns = $"{x.InsertDate:dd/MM/yyyy}", OraIns = $"{x.InsertDate:HH:mm}", Lavorazione = $"{getLV(dictAF, x.KeyRichiesta)} {x.Note}".Trim() })
|
|
.ToList();
|
|
|
|
// predispongo dati articoli
|
|
ListaArticoli = listaArt
|
|
.Select(a => new ArtRow() { Matricola = a.CodArticolo, Articolo = a.Disegno, Descrizione = a.DescArticolo, LimiteVel = 100 })
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
// completo con dati DOSSIER
|
|
foreach (var item in ListaArticoli)
|
|
{
|
|
string codArt = item.Matricola;
|
|
var currDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
|
|
if (currDoss != null)
|
|
{
|
|
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(currDoss.Valore);
|
|
// traduco AD MENTULAM...
|
|
item.Peso_01 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso1");
|
|
item.Peso_02 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso2");
|
|
item.Peso_03 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso3");
|
|
item.Peso_04 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso4");
|
|
item.PosizCarrello_01 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr1");
|
|
item.PosizCarrello_02 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr2");
|
|
item.PosizCarrello_03 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr3");
|
|
item.PosizCarrello_04 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr4");
|
|
}
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua upload verso server FTP della macchina dei files nella folder indicata
|
|
/// </summary>
|
|
/// <param name="folderDir"></param>
|
|
/// <returns></returns>
|
|
protected override bool iobSendFTP(string folderDir)
|
|
{
|
|
bool answ = false;
|
|
//leggo CONF
|
|
string ftpServ = getOptPar("FTP_SERVER");
|
|
string ftpUser = getOptPar("FTP_USER");
|
|
string ftpPass = getOptPar("FTP_PWD");
|
|
string ftpCert = getOptPar("FTP_CERT");
|
|
string doSkip = getOptPar("FTP_SKIP");
|
|
bool ftpSkip = false;
|
|
bool.TryParse(doSkip, out ftpSkip);
|
|
var ftpClient = new Manager(ftpServ, ftpUser, ftpPass, ftpCert, ftpSkip);
|
|
var testServer = ftpClient.serverOk();
|
|
if (testServer)
|
|
{
|
|
lg.Info($"FTP: server found at {ftpServ}");
|
|
var srvType = ftpClient.serverType();
|
|
lg.Info($"FTP Server type: {srvType}");
|
|
|
|
string remDir = "data/test_directory";
|
|
string locDir = "temp/";
|
|
var preTest = ftpClient.dirExists(remDir);
|
|
if (!preTest)
|
|
{
|
|
var dirCreate = ftpClient.createDir(remDir);
|
|
lg.Info($"FTP: created remote dir {remDir}");
|
|
}
|
|
string basePath = Directory.GetCurrentDirectory();
|
|
string localPath = Path.Combine(basePath, locDir);
|
|
var dirUploaded = ftpClient.sendDir(localPath, remDir);
|
|
if (dirUploaded)
|
|
{
|
|
lg.Info($"FTP: uploaded dir content {locDir} --> {remDir}");
|
|
}
|
|
// se ok --> sposto invio
|
|
DateTime adesso = DateTime.Now;
|
|
string archPath = Path.Combine(basePath, "DATA", "HIST", $"{adesso:yyyy}");
|
|
if (!Directory.Exists(archPath))
|
|
{
|
|
Directory.CreateDirectory(archPath);
|
|
}
|
|
Directory.Move(localPath, Path.Combine(archPath, $"{adesso:MMddHHmmss}"));
|
|
lg.Info($"FTP: Archived dir content {locDir} --> {adesso:MMddHHmmss}");
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prepara files CSV da inviare alla macchina
|
|
/// </summary>
|
|
protected override bool iobWriteLocalCSV()
|
|
{
|
|
bool answ = false;
|
|
// salvo articoli
|
|
string basePath = Directory.GetCurrentDirectory();
|
|
string tempDir = Path.Combine(basePath, "temp");
|
|
if (!Directory.Exists(tempDir))
|
|
{
|
|
Directory.CreateDirectory(tempDir);
|
|
lg.Info($"CSV: created local dir {tempDir}");
|
|
}
|
|
string filePath = Path.Combine(tempDir, "articoli.csv");
|
|
answ = DataExport.SaveToCsv(ListaArticoli, filePath);
|
|
if (answ)
|
|
{
|
|
lg.Info("CSV: created ART file as articoli.csv");
|
|
// salvo PODL
|
|
string csvName = $"{DateTime.Now:dd-MM-yyyy}.csv";
|
|
filePath = Path.Combine(tempDir, $"{csvName}");
|
|
answ = DataExport.SaveToCsv(ListaJobs, filePath);
|
|
if (answ)
|
|
{
|
|
lg.Info($"CSV: created PODL file as {csvName}");
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// Azioni specifiche x indicare fine lotto di produzione
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool setFineLotto()
|
|
{
|
|
bool answ = false;
|
|
try
|
|
{
|
|
List<WriteValue> nodes2Write = new List<WriteValue>();
|
|
foreach (var item in opcUaParams.actStopProd)
|
|
{
|
|
WriteValue commWriteVal = new WriteValue();
|
|
commWriteVal.NodeId = new NodeId(item.Key);
|
|
commWriteVal.AttributeId = Attributes.Value;
|
|
commWriteVal.Value = new DataValue();
|
|
commWriteVal.Value.Value = item.Value;
|
|
|
|
nodes2Write.Add(commWriteVal);
|
|
}
|
|
// vera scrittura
|
|
UA_ref.WriteNodes(nodes2Write);
|
|
answ = true;
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Azioni specifiche x iniziare produzione (impostazione ricetta)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool setInizioProd()
|
|
{
|
|
bool answ = false;
|
|
try
|
|
{
|
|
List<WriteValue> nodes2Write = new List<WriteValue>();
|
|
foreach (var item in opcUaParams.actSetRecipe)
|
|
{
|
|
WriteValue commWriteVal = new WriteValue();
|
|
commWriteVal.NodeId = new NodeId(item.Key);
|
|
commWriteVal.AttributeId = Attributes.Value;
|
|
commWriteVal.Value = new DataValue();
|
|
commWriteVal.Value.Value = item.Value;
|
|
|
|
nodes2Write.Add(commWriteVal);
|
|
}
|
|
// vera scrittura
|
|
UA_ref.WriteNodes(nodes2Write);
|
|
answ = true;
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
} |