using Microsoft.AspNetCore.Mvc;
using MP.Core.DTO;
using MP.Core.Objects;
using MP.IOC.Data;
using Newtonsoft.Json;
using NLog;
using System.Reflection;
using static MP.Core.Objects.Enums;
namespace MP.IOC.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class IOBController : ControllerBase
{
#region Public Constructors
public IOBController(IConfiguration configuration, MpDataService DataService)
{
_configuration = configuration;
DService = DataService;
}
#endregion Public Constructors
#region Public Methods
/// SALVA x macchina KVP parametro/valore:
/// GET: api/IOB/addOptPar/SIMUL_03?pName=PZREQ&pValue=1000
///
[HttpGet("addOptPar/{id}")]
public async Task AddOptPar(string id, string pName, string pValue)
{
DService.ScriviKeepAlive(id, DateTime.Now);
try
{
DService.AddOptPar4Machine(id, pName, pValue);
return await GetOptPar(id);
}
catch
{
return StatusCode(StatusCodes.Status500InternalServerError, "Errore interno | AddOptPar");
}
}
///
/// GET: IOB/
///
///
[HttpGet]
public IActionResult Alive()
{
return Ok("OK"); // Restituisce Status 200
}
///
/// GET: IOB/enabled/SIMUL_03
///
///
///
[HttpGet("enabled/{id}")]
public IActionResult Enabled(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
if (DService.IobInsEnab(id))
{
return Ok("OK");
}
else
{
//return StatusCode(503, "NO");
return UnprocessableEntity("NO");
}
}
///
/// Invio record flog direttamente via URL GET
/// GET: IOB/flog/SIMUL_03?flux=PROG&valore=P0001&dtEve=20161223180600000&dtCurr=20161223180600000&cnt=999&disabKA=false
///
/// IdxMacchina
/// CodFlux
/// Valore da registrare
/// DataOra evento (IOB time)
/// DataOra corrente (IOB time)
/// Contatore eventi (IOB)
/// Disabilitazione scrittura record KeepAlive
///
[HttpGet("flog/{id}")]
public async Task Flog(string id, string flux, string valore, string dtEve, string dtCurr, string cnt = "0", string disabKA = "false")
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "";
// formato yyyymmddHHMMSSnnn ovvero da anno a millisecondi
DateTime dataOraEvento = DateTime.Now;
Log.Trace($"Valori letti: id: {id} | flux: {flux} valore: {valore}");
try
{
int count = 0;
Int32.TryParse(cnt, out count);
bool disKa = false;
bool.TryParse(disabKA, out disKa);
answ = await DService.ProcessFluxLogAsync(id, flux, valore, dtEve, dtCurr, count, disKa);
return Ok(answ);
}
catch (Exception exc)
{
Log.Error($"Errore in flog{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// Processa una chiamata POST per l'invio di un array Json di oggetti fluxLog
/// PUT: IOB/flogJson/SIMUL_03
///
/// ID dell'IOB
/// Disabilitazione della scrittura del record KeepAlive
///
[HttpPost("flogJson/{id}")]
public async Task FlogJson(string id, string disabKA = "false", [FromBody] string content = "")
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
bool disableKa = false;
bool.TryParse(disabKA, out disableKa);
string answ = "-";
// se ho dati...
if (content != "")
{
answ = await processFLogJsonAsync(id, disableKa, content);
return Ok(answ);
}
else
{
Log.Error($"Errore in FlogJson - no content");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// Recupera COUNTER x macchina:
/// GET: IOB/getCounter/SIMUL_03
///
///
/// Il conteggio pezzi o un errore strutturato
[HttpGet("getCounter/{id}")]
public async Task GetCounter(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
try
{
var pzCount = await DService.pzCounter(id);
return Ok(pzCount);
}
catch (Exception exc)
{
Log.Error(exc, "Errore durante il recupero del counter per la macchina {MachineId}", id);
return StatusCode(StatusCodes.Status500InternalServerError, "Errore interno | GetCounter");
}
}
///
/// Recupera COUNTER x macchina dal CONTEGGIO dei TCRecorded:
///
/// GET: IOB/getCounterTCRec/5
///
///
/// Il conteggio pezzi o un errore strutturato
[HttpGet("getCounterTCRec/{id}")]
public async Task GetCounterTCRec(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
try
{
var pzCount = await DService.pzCounterTcAsync(id);
return Ok(pzCount);
}
catch (Exception exc)
{
Log.Error(exc, "Errore durante il recupero del counter TC per la macchina {MachineId}", id);
//return StatusCode(StatusCodes.Status500InternalServerError, "NO");
return StatusCode(StatusCodes.Status500InternalServerError, "Errore interno | GetCounterTCRec");
}
}
///
/// GET elenco parametri correnti x IOB
///
///
///
[HttpGet("getCurrObjItems/{id}")]
public async Task GetCurrObjItems(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
var rawData = await DService.MachineParamListAsync(id);
List actValues = new List();
// ordino!
actValues = rawData
.OrderBy(x => x.displOrdinal)
.ThenBy(x => x.description)
.ToList();
return Ok(actValues);
}
///
/// Recupera ODL corrente x macchina:
/// GET: IOB/getCurrODL/SIMUL_03
///
///
///
[HttpGet("getCurrODL/{id}")]
public async Task GetCurrODL(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
try
{
var odl = await DService.GetCurrOdlAsync(id);
return Ok($"{odl}");
}
catch (Exception exc)
{
Log.Error(exc, "Errore GetCurrODL | macchina {MachineId}", id);
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
//return StatusCode(StatusCodes.Status500InternalServerError, "Errore interno | GetCurrODL");
}
}
///
/// Restituisce la quantità pezzi dell'odl correntemente in lavorazione sulla macchina...
/// GET: IOB/getCurrOdlQtaReq/SIMUL_01
///
///
///
[HttpGet("getCurrOdlQtaReq/{id}")]
public async Task GetCurrOdlQtaReq(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
int answ = 0;
// chiamo metodo redis/db...
try
{
var odlData = await DService.OdlCurrByMaccAsync(id);
if (odlData != null && odlData.DataInizio.HasValue)
{
answ = odlData.NumPezzi;
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in recupero GetCurrOdlQtaReq{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
return Ok(answ);
}
///
/// Restituisce data-ora inizio dell'odl correntemente in lavorazione sulla macchina...
/// es: http://url_site/MP/IO/IOB/getCurrOdlStart/SIMUL_03
///
///
///
[HttpGet("getCurrOdlStart/{id}")]
public async Task GetCurrOdlStart(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
DateTime answ = new DateTime(DateTime.Now.Year - 1, 12, 31);
// chiamo metodo redis/db...
try
{
var odlData = await DService.OdlCurrByMaccAsync(id);
if (odlData != null && odlData.DataInizio.HasValue)
{
answ = odlData.DataInizio.Value;
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in recupero getCurrOdlStart{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
return Ok(answ.ToString("yyyy-MM-dd HH:mm:ss"));
}
///
/// Recupera TASK richiesto x macchina:
/// GET: IOB/getOptPar/SIMUL_03
///
///
/// Json contenente 1..n task da eseguire
[HttpGet("getOptPar/{id}")]
public async Task GetOptPar(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "";
// scrivo keep alive!!! (se necessario, altrimenti è in cache...)
DService.ScriviKeepAlive(id, DateTime.Now);
try
{
// leggo da REDIS eventuale elenco task x macchina...
Dictionary valori = DService.mOptParMacchina(id);
answ = JsonConvert.SerializeObject(valori);
}
catch
{ }
return Ok(answ);
}
///
/// Recupera TASK richiesto x macchina:
/// GET: IOB/getTask2Exe/SIMUL_03
///
///
/// Json contenente 1..n task da eseguire
[HttpGet("getTask2Exe/{id}")]
public async Task GetTask2Exe(string id)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "";
DService.ScriviKeepAlive(id, DateTime.Now);
// leggo da REDIS eventuale elenco task x macchina...
Dictionary valori = await DService.GetTask2ExeMacchinaAsync(id);
answ = JsonConvert.SerializeObject(valori);
return Ok(answ);
}
///
/// Metodo di dichiarazione "improprio" tramite get call totalmetne definita da URL
/// GET: IOB/input/SIMUL_03?valore=3&dtEve=20181206180600000&dtCurr=20181206180600000&cnt=999
///
///
///
///
///
///
///
[HttpGet("input/{id}")]
public async Task Input(string id, string valore, string dtEve, string dtCurr, string cnt)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
string answ = "";
if (cnt == null)
{
cnt = "0";
}
DateTime dataOraEvento = DateTime.Now;
try
{
answ = DService.processInput(id, valore, dtEve, dtCurr, cnt);
return Ok(answ);
}
catch (Exception exc)
{
Log.Error($"Errore in processInput{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// Processa una chiamata POST per l'invio di una Dictionary HashList da salvare in redis
/// POST: IOB/saveMachineIobConf/SIMUL_03
///
/// ID dell'IOB
///
[HttpPost("saveMachineIobConf/{id}")]
public async Task SaveMachineIobConf(string id, [FromBody] Dictionary currDict)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "NA";
try
{
bool fatto = await DService.SaveMachineIobConf(id, currDict);
answ = fatto ? "OK" : "KO";
}
catch (Exception exc)
{
Log.Error($"Errore in saveMachineIobConf{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
return Ok(answ);
}
///
/// SALVA in blocco un incremento pezzi x macchina restituendo il valore appena inviato o,
/// se mancasse chaive redis, del valore da DB
///
/// GET: IOB/savePzCountInc/5?qty=10
///
/// codice macchina
/// num peziz da salvare in blocco
///
[HttpGet("savePzCountInc/{id}")]
public async Task SavePzCountInc(string id, string qty)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "";
DateTime dataOraEvento = DateTime.Now;
// salvo SEMPRE log x questo tipo di dati!
Log.Info($"Salvataggio incremento contapezzi | id: {id} | pezzi: {qty}");
try
{
answ = await DService.saveCaricoPezzi(id, qty);
return Ok(answ);
}
catch (Exception exc)
{
Log.Error($"Errore in savePzCountInc{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// SALVA Counter x macchina restituendo il valore appena inviato o, se mancasse chiave
/// redis, del valore da DB
///
/// GET: IOB/setCounter/5?counter=10
///
/// cod macchina
/// contapezzi da salvare
///
[HttpGet("setCounter/{id}")]
public async Task SetCounter(string id, string counter)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "-1";
DateTime dataOraEvento = DateTime.Now;
Log.Debug($"Salvataggio counter | id: {id} | pzCount: {counter}");
answ = await DService.saveCounterAsync(id, counter);
return Ok(answ);
}
///
/// Salva associazione tra macchina, device IOB chiamante e sue info
///
/// Id della macchina
/// Nome dell'IOB di acquisizione della macchina
///
[HttpGet("setM2IOB/{id}")]
public async Task SetM2IOB(string id, string IOB_name)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
try
{
// recupero IP del client remoto
var agent = Request.Headers["User-Agent"].ToString();
var IPv4 = HttpContext.Connection.RemoteIpAddress?.ToString();
// creo oggetto IOB_data...
IOB_data m2IOB = new IOB_data
{
name = IOB_name,
IP = IPv4,
iType = IobType.ND,
typeCss = "fa fa-question-circle-o",
CNC_Counter = false
};
// imposto tipo ed icona come windows/linux secondo UserAgent...
if (agent.IndexOf("WIN") >= 0)
{
m2IOB.iType = IobType.WIN;
m2IOB.typeCss = "fa fa-windows";
m2IOB.CNC_Counter = true;
}
else if (agent.IndexOf("Python") >= 0)
{
m2IOB.iType = IobType.rPi;
m2IOB.typeCss = "fa fa-linux";
}
// serializzo...
string dataSer = JsonConvert.SerializeObject(m2IOB);
// salvo...
await DService.SaveMachine2Iob(id, dataSer);
// salvo tutto OK
return Ok("OK");
}
catch (Exception exc)
{
Log.Error($"Errore in SetM2IOB{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// Processa una chiamata GET x salvare UserLog
/// GET: IOB/ulog/SIMUL_03?flux=PROG&valore=P0001&dtEve=20161223180600000&dtCurr=20161223180600000&cnt=999&matrOpr=0=0&label=&valNum
///
///
///
///
///
///
///
///
///
///
///
[HttpGet("ulog/{id}")]
public async Task ULog(string id, string flux, string valore, string dtEve, string dtCurr, string cnt, string matrOpr, string label, string valNum)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "";
// formato yyyymmddHHMMSSnnn ovvero da anno a millisecondi
if (string.IsNullOrEmpty(cnt))
{
cnt = "0";
}
DateTime dataOraEvento = DateTime.Now;
try
{
int count = 0;
int nMatrOpr = 0;
int nValNum = 0;
Int32.TryParse(cnt, out count);
Int32.TryParse(matrOpr, out nMatrOpr);
Int32.TryParse(valNum, out nValNum);
answ = await DService.ProcessUserLogAsync(id, flux, valore, dtEve, dtCurr, count, nMatrOpr, label, nValNum);
return Ok(answ);
}
catch (Exception exc)
{
Log.Error($"Errore in ulog{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// Processa una chiamata POST per l'invio di una List Json 1+ UserAction (contiene
/// controlli, scarti, dichiarazioni)
/// POST: IOB/ulogJson/SIMUL_03
///
/// ID dell'IOB
///
[HttpPost("ulogJson/{id}")]
public async Task ulogJson(string id, [FromBody] string content = "")
{
string answ = "-";
// se ho dati...
if (content != "")
{
answ = await processULogJsonAsync(id, content);
return Ok(answ);
}
else
{
Log.Error($"Errore in ULogJson - no content");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
}
///
/// Processa una chiamata POST per l'invio di una List Json di UNO O PIU' oggetti objItem
/// POST: IOB/upsertObjItems/SIMUL_03
///
/// ID dell'IOB
///
[HttpPost("upsertObjItems/{id}")]
public async Task UpsertObjItems(string id, [FromBody] List innovazioni)
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "NA";
try
{
// se != null --> salvo!
if (innovazioni != null)
{
// salvo
bool fatto = await DService.MachineParamUpsertAsync(id, innovazioni);
answ = "OK";
}
}
catch (Exception exc)
{
Log.Error($"Errore in upsertObjItems{Environment.NewLine}{exc}");
return StatusCode(StatusCodes.Status500InternalServerError, "NO");
}
return Ok(answ);
}
///
/// Verifica versione corrente REST API
///
///
[HttpGet("version")]
public IActionResult Version()
{
var version = Assembly
.GetExecutingAssembly()
.GetName()
.Version?
.ToString() ?? "unknown";
return Ok(version);
}
#endregion Public Methods
#region Private Fields
private static IConfiguration _configuration = null!;
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
#region Private Properties
///
/// Dataservice x accesso DB
///
private MpDataService DService { get; set; }
#endregion Private Properties
#region Private Methods
///
/// Effettivo processing FLogJson
///
///
///
///
///
///
private async Task processFLogJsonAsync(string idxMacc, bool disabKA, string content)
{
string answ = "";
int insDone = 0;
// procedo a deserializzare in blocco l'oggetto...
FLogJsonPayloadDto receivedData = new FLogJsonPayloadDto();
try
{
// deserializzo.
receivedData = JsonConvert.DeserializeObject(content) ?? new();
}
catch (Exception exc)
{
Log.Error($"Errore in fase deserializzazione flogJson{Environment.NewLine}{exc}");
answ = "NO";
}
// se ho qualcosa da processare...
if (receivedData != null)
{
// per ogni valore --> salvo!
try
{
foreach (var item in receivedData.fluxData)
{
// formato datetime come yyyyMMddHHmmssfff -->es: 20181223180600000
answ = await DService.ProcessFluxLogAsync(idxMacc, item.flux, item.valore, item.dtEve.ToString("yyyyMMddHHmmssfff"), item.dtCurr.ToString("yyyyMMddHHmmssfff"), item.cnt, disabKA);
}
// se vuoto --> OK!
if (string.IsNullOrEmpty(answ))
{
answ = $"OK {insDone} processed";
}
}
catch (Exception exc)
{
Log.Error($"Errore in fase invio valori flogJson{Environment.NewLine}{exc}");
answ = "NO";
}
// leggo parametri correnti...
DateTime adesso = DateTime.Now;
try
{
List currParams = DService.MachineParamList(idxMacc);
// ora per ogni valore RICEVUTO costruisco un oggetto in innovazioni da
// inviare...x salvare in stato parametri...
List innovazioni = new List();
foreach (var item in receivedData.fluxData)
{
// flux = uuid del parametro
ObjItemDTO? trovato = currParams.Find(obj => obj.uid == item.flux);
// se lo trovo aggiorno...
if (trovato != null)
{
// aggiorno valore e data
trovato.value = item.valore;
trovato.lastRead = adesso;
trovato.lastMessage = item.valore;
// se fosse un valore WRITE
if (trovato.writable)
{
// ...e mi ha dato un valore vuoto --> mando un fix x riscrittura
if (string.IsNullOrEmpty(item.valore))
{
Log.Debug($"flogJson | verifica parametri | {trovato.uid} | reqVal: {trovato.reqValue}");
taskType currTask = (taskType)Enum.Parse(typeof(taskType), trovato.uid);
DService.AddCheckTask4Machine(idxMacc, currTask, item.valore);
}
}
else
{
// richiesto 10 sec prima...
trovato.lastRequest = adesso.AddSeconds(-10);
}
}
// altrimenti AGGIUNGO (READ ONLY)...
else
{
trovato = new ObjItemDTO
{
uid = item.flux,
name = item.flux,
value = item.valore,
lastRead = adesso,
lastRequest = adesso.AddSeconds(-10),
writable = false
};
}
// lo carico in innovation
innovazioni.Add(trovato);
}
// faccio upsert innovations!
DService.UpsertCurrObjItems(idxMacc, innovazioni);
Log.Info($"processFLogJsonAsync | {idxMacc} | Updated {innovazioni.Count} par");
}
catch (Exception exc)
{
Log.Error($"Errore in fase salvataggio innovazioni parametri correnti da flogJson{Environment.NewLine}{exc}");
answ = "NO";
}
}
return answ;
}
///
/// Effettua processing UserLog
///
///
///
///
private async Task processULogJsonAsync(string id, string content)
{
string answ = "";
int insDone = 0;
// procedo a deserializzare in blocco l'oggetto...
ULogJsonPayloadDto receivedData = new ULogJsonPayloadDto();
try
{
// deserializzo.
receivedData = JsonConvert.DeserializeObject(content) ?? new();
}
catch (Exception exc)
{
Log.Error($"Errore in fase deserializzazione ulogJson{Environment.NewLine}{exc}");
answ = "NO";
}
// se ho qualcosa da processare...
if (receivedData != null)
{
// per ogni valore --> salvo!
try
{
foreach (var item in receivedData.fluxData)
{
// formato datetime come yyyyMMddHHmmssfff -->es: 20181223180600000
answ = await DService.ProcessUserLogAsync(id, item.flux, item.valore, item.dtEve.ToString("yyyyMMddHHmmssfff"), item.dtCurr.ToString("yyyyMMddHHmmssfff"), item.cnt, item.matrOpr, item.label, item.valNum);
}
// se vuoto --> OK!
if (string.IsNullOrEmpty(answ))
{
answ = $"OK {insDone} processed";
}
}
catch (Exception exc)
{
Log.Error($"Errore in fase invio valori ulogJson{Environment.NewLine}{exc}");
answ = "NO";
}
}
return answ;
}
#endregion Private Methods
}
}