Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobOpc/OpcUaOmronIcoel.cs
T
Samuele Locatelli 8bb0f158b5 SPLIT PROGETTO!!!
- proj di base con le 2 form da ereditare
- progetto globale che contiene TUTTI gli adapter (pronto a venire spezzettato
- gettate le basi x "portare fuori" i vari componenti oppure fare compilazione condizonale
2024-12-20 10:16:32 +01:00

661 lines
26 KiB
C#

using IOB_UT_NEXT;
using MapoSDK;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
namespace IOB_WIN_NEXT.IobOpc
{
public enum IcoelStatus
{
Allarme = 0,
Stop = 1,
Manuale = 2,
Automatico = 3
}
public class DatiMesIcoel
{
#region Public Constructors
/// <summary>
/// Inizializzazione classe con dati RAW da byte[]
/// </summary>
/// <param name="rawData">Flusso di dati RAW da tradurre...</param>
public DatiMesIcoel(byte[] rawData)
{
// solo se i dati sono esattamente 115...
if (rawData.Length >= 115)
{
Calibratrice_L1 = new MesItemStatus(rawData.Skip(0).Take(7).ToArray());
Calibratrice_L2 = new MesItemStatus(rawData.Skip(7).Take(7).ToArray());
Dewatering_L1 = new MesItemStatus(rawData.Skip(14).Take(7).ToArray());
Dewatering_L2 = new MesItemStatus(rawData.Skip(21).Take(7).ToArray());
Precalibro_L1 = new MesItemStatus(rawData.Skip(28).Take(7).ToArray());
Precalibro_L2 = new MesItemStatus(rawData.Skip(35).Take(7).ToArray());
Taglierina_L1 = new MesItemStatus(rawData.Skip(42).Take(7).ToArray());
Taglierina_L2 = new MesItemStatus(rawData.Skip(49).Take(7).ToArray());
NastroTaglierina_L1 = new MesItemStatus(rawData.Skip(56).Take(7).ToArray());
NastroTaglierina_L2 = new MesItemStatus(rawData.Skip(63).Take(7).ToArray());
Elevatore_L1 = new MesItemStatus(rawData.Skip(70).Take(7).ToArray());
Elevatore_L2 = new MesItemStatus(rawData.Skip(77).Take(7).ToArray());
ImmergitoreBins_L1 = new MesItemStatus(rawData.Skip(84).Take(7).ToArray());
ImmergitoreBins_L2 = new MesItemStatus(rawData.Skip(91).Take(7).ToArray());
ImmergitoreCasse_L1 = new MesItemStatus(rawData.Skip(98).Take(7).ToArray());
ImmergitoreCasse_L2 = new MesItemStatus(rawData.Skip(105).Take(7).ToArray());
Varie = new PlantStatus(rawData.Skip(112).Take(3).ToArray());
}
}
#endregion Public Constructors
#region Public Properties
public MesItemStatus Calibratrice_L1 { get; set; }
public MesItemStatus Calibratrice_L2 { get; set; }
public MesItemStatus Dewatering_L1 { get; set; }
public MesItemStatus Dewatering_L2 { get; set; }
public MesItemStatus Elevatore_L1 { get; set; }
public MesItemStatus Elevatore_L2 { get; set; }
public MesItemStatus ImmergitoreBins_L1 { get; set; }
public MesItemStatus ImmergitoreBins_L2 { get; set; }
public MesItemStatus ImmergitoreCasse_L1 { get; set; }
public MesItemStatus ImmergitoreCasse_L2 { get; set; }
public MesItemStatus NastroTaglierina_L1 { get; set; }
public MesItemStatus NastroTaglierina_L2 { get; set; }
public MesItemStatus Precalibro_L1 { get; set; }
public MesItemStatus Precalibro_L2 { get; set; }
public MesItemStatus Taglierina_L1 { get; set; }
public MesItemStatus Taglierina_L2 { get; set; }
public PlantStatus Varie { get; set; }
#endregion Public Properties
#region Public Methods
public override bool Equals(object obj)
{
// Object is not a GaugeModel instance
if (!(obj is DatiMesIcoel item))
return false;
if (Calibratrice_L1 != item.Calibratrice_L1)
return false;
if (Calibratrice_L2 != item.Calibratrice_L2)
return false;
if (Dewatering_L1 != item.Dewatering_L1)
return false;
if (Dewatering_L2 != item.Dewatering_L2)
return false;
if (Precalibro_L1 != item.Precalibro_L1)
return false;
if (Precalibro_L2 != item.Precalibro_L2)
return false;
if (Taglierina_L1 != item.Taglierina_L1)
return false;
if (Taglierina_L2 != item.Taglierina_L2)
return false;
if (NastroTaglierina_L1 != item.NastroTaglierina_L1)
return false;
if (NastroTaglierina_L2 != item.NastroTaglierina_L2)
return false;
if (Elevatore_L1 != item.Elevatore_L1)
return false;
if (Elevatore_L2 != item.Elevatore_L2)
return false;
if (ImmergitoreBins_L1 != item.ImmergitoreBins_L1)
return false;
if (ImmergitoreBins_L2 != item.ImmergitoreBins_L2)
return false;
if (ImmergitoreCasse_L1 != item.ImmergitoreCasse_L1)
return false;
if (ImmergitoreCasse_L2 != item.ImmergitoreCasse_L2)
return false;
if (Varie != item.Varie)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion Public Methods
}
public class MesItemStatus
{
#region Public Constructors
public MesItemStatus(byte[] rawData)
{
Stato = (IcoelStatus)BitConverter.ToUInt16(rawData, 0);
Velocita = BitConverter.ToUInt16(rawData, 2);
Termico = !rawData.Skip(4).Take(1).FirstOrDefault().Equals(0);
MagnetoTermico = !rawData.Skip(5).Take(1).FirstOrDefault().Equals(0);
AvariaInverter = !rawData.Skip(6).Take(1).FirstOrDefault().Equals(0);
}
#endregion Public Constructors
#region Public Properties
public bool AvariaInverter { get; set; } = false;
public bool MagnetoTermico { get; set; } = false;
public IcoelStatus Stato { get; set; } = 0;
public bool Termico { get; set; } = false;
public UInt16 Velocita { get; set; } = 0;
#endregion Public Properties
#region Public Methods
public override bool Equals(object obj)
{
// Object is not a GaugeModel instance
if (!(obj is MesItemStatus item))
return false;
if (Stato != item.Stato)
return false;
if (Velocita != item.Velocita)
return false;
if (Termico != item.Termico)
return false;
if (MagnetoTermico != item.MagnetoTermico)
return false;
if (AvariaInverter != item.AvariaInverter)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// Converte un singolo item in un array di byte per scrittura su PLC S7
/// </summary>
/// <returns></returns>
public byte[] serialize()
{
byte[] answ = new byte[7];
Buffer.BlockCopy(BitConverter.GetBytes((short)Stato), 0, answ, 0, 2);
Buffer.BlockCopy(BitConverter.GetBytes(Velocita), 0, answ, 2, 2);
Buffer.BlockCopy(BitConverter.GetBytes(Termico), 0, answ, 4, 1);
Buffer.BlockCopy(BitConverter.GetBytes(MagnetoTermico), 0, answ, 5, 1);
Buffer.BlockCopy(BitConverter.GetBytes(AvariaInverter), 0, answ, 6, 1);
return answ;
}
#endregion Public Methods
}
public class OpcUaOmronIcoel : OpcUaOmron
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per Omron (es ICOEL) https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaOmronIcoel(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
doByteRead = true;
lgInfo($"Avviato IobOpcUaOmronIcoel | encodeReadData: {doByteRead}");
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
#if false
if (utils.CRB("enableContapezzi"))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
#endif
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Internal Methods
/// <summary>
/// Verifica ed invia variazioni DAL FORMATO RAW data (byte[]) --&gt; esplode oggetti e li
/// testa 1:1
/// </summary>
/// <param name="MonIt"></param>
/// <param name="NotifyValue"></param>
/// <param name="forceSend"></param>
internal override bool checkAndSendRaw(Opc.Ua.Client.MonitoredItem MonIt, byte[] NotifyValue, bool forceSend)
{
bool changed = false;
if (MonIt != null)
{
if (NotifyValue != null && NotifyValue.Length > 0)
{
// verifico variazione "globale"
if (!lastData.Equals(currData))
{
// effettuo test/invio x ogni info
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L1", lastData.Calibratrice_L1, currData.Calibratrice_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L2", lastData.Calibratrice_L2, currData.Calibratrice_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Dewatering_L1", lastData.Dewatering_L1, currData.Dewatering_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Dewatering_L2", lastData.Dewatering_L2, currData.Dewatering_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Elevatore_L1", lastData.Elevatore_L1, currData.Elevatore_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Elevatore_L2", lastData.Elevatore_L2, currData.Elevatore_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L1", lastData.ImmergitoreBins_L1, currData.ImmergitoreBins_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L2", lastData.ImmergitoreBins_L2, currData.ImmergitoreBins_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L1", lastData.ImmergitoreCasse_L1, currData.ImmergitoreCasse_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L2", lastData.ImmergitoreCasse_L2, currData.ImmergitoreCasse_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L1", lastData.NastroTaglierina_L1, currData.NastroTaglierina_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L2", lastData.NastroTaglierina_L2, currData.NastroTaglierina_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Precalibro_L1", lastData.Precalibro_L1, currData.Precalibro_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Precalibro_L2", lastData.Precalibro_L2, currData.Precalibro_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Taglierina_L1", lastData.Taglierina_L1, currData.Taglierina_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Taglierina_L2", lastData.Taglierina_L2, currData.Taglierina_L2, forceSend);
// salvo lastData...
lastData = currData;
}
}
else
{
lgError($"checkAndSend ERROR | MonIt: {MonIt.DisplayName} | NotifyValue Null!!!");
}
}
else
{
lgError("checkAndSend ERROR: MonIt null");
}
return changed;
}
internal override void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e)
{
base.UA_ref_eh_MonItChange(sender, e);
}
#endregion Internal Methods
#region Protected Properties
/// <summary>
/// Valore corrente dei dati ICOEL (traduzione JIT da byte[])
/// </summary>
protected DatiMesIcoel currData
{
get
{
byte[] rawByte = new byte[115];
// se ho dati raw decodifico...
if (byteRawData != null)
{
if (byteRawData.Length >= 115)
{
rawByte = byteRawData;
}
}
DatiMesIcoel answ = new DatiMesIcoel(rawByte);
return answ;
}
}
/// <summary>
/// Indica se abbia stato POWER ON (multicondizione)
/// </summary>
protected override bool hasPowerOn
{
get
{
// da rivedere
return true;
//return checkMultiCondition(opcUaParams.condPowerOn);
}
}
/// <summary>
/// Indica se abbia stato MANUAL (condizioni varie, es stopped)
/// </summary>
protected override bool isManual
{
get
{
return false;
//return checkMultiCondition(opcUaParams.condManual);
}
}
/// <summary>
/// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
/// </summary>
protected override bool isReady
{
get
{
return false;
//return checkMultiCondition(opcUaParams.condReady);
}
}
/// <summary>
/// Ultima versione validata delle info x confronto
/// </summary>
protected DatiMesIcoel lastData { get; set; } = new DatiMesIcoel(new byte[115]);
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
// decodifico da currData
if (!currData.Varie.InEmergenza)
{
B_input += (1 << 7);
}
if (currData.Varie.InMarcia)
{
B_input += (1 << 1);
}
if (currData.Varie.InStop)
{
B_input += (1 << 3);
}
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
}
/// <summary>
/// effettua verifica del datablock icoel inviando eventualmente i dati variati
/// </summary>
/// <param name="startNodeId"></param>
/// <param name="blockName"></param>
/// <param name="currBlock"></param>
/// <param name="newBlock"></param>
/// <param name="forceSend"></param>
/// <returns></returns>
protected bool testSendDataBlock(NodeId startNodeId, string blockName, MesItemStatus currBlock, MesItemStatus newBlock, bool forceSend)
{
bool changed = false;
// verifica globale blocchi old/new...
if (!currBlock.Equals(newBlock))
{
// creo un nuovo monitoredItem se non ci fosse x ogni variabile dell'oggetto...
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Stato", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Stato}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Velocita", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Velocita}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Termico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Termico}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_MagnetoTermico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.MagnetoTermico}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_AvariaInverter", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.AvariaInverter}", forceSend);
// spostare sotto in checkAndSendRaw ??? FIXME todo
if (changed)
{
lgTrace($"Invio variazione dataitem per {dataItemMem.Count} elementi");
sendDataItemListToServer(startNodeId);
}
else
{
lgTrace("Nessuna variazione DataItem da trasmettere");
}
}
return changed;
}
/// <summary>
/// Verifico se salvare e inviare proprietà specificata
/// </summary>
/// <param name="MonIt"></param>
protected bool testSendProperty(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
{
bool changed = false;
DateTime locTStamp = DateTime.Now;
string sVal = "";
string descr = "";
string uuid = calcID($"{MonIt.StartNodeId.Identifier}", $"{MonIt.DisplayName}");
descr = itemTranslation("OPC", uuid);
sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {NotifyValue}";
lgDebug($"TSP | {sVal}");
changed = checkSaveValue(MonIt, NotifyValue, false);
// cerco se non sia un dato filtrato in FLUXLOG...
bool isFiltered = opcUaParams.fluxLogVeto.Contains(uuid);
if (isFiltered)
{
lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false);
}
else
{
if (changed || forceSend)
{
accodaFLog(sVal, qEncodeFLog(descr, $"{NotifyValue}"));
}
else
{
lgTrace($"TSP | NON ACCODATO sample per {uuid} - verifica variazione ha dato esito negativo", false);
}
}
return changed;
}
#endregion Protected Methods
}
public class PlantStatus
{
#region Public Constructors
public PlantStatus(byte[] rawData)
{
InMarcia = !rawData.Skip(0).Take(1).FirstOrDefault().Equals(0);
InEmergenza = !rawData.Skip(1).Take(1).FirstOrDefault().Equals(0);
InStop = !rawData.Skip(2).Take(1).FirstOrDefault().Equals(0);
//var valore = BitConverter.ToUInt16(rawData, 0);
//byte b = rawData.Skip(0).Take(1).FirstOrDefault();
//InMarcia = (b & (1 << (1 - 1))) != 0;
//InEmergenza = (b & (1 << (2 - 1))) != 0;
//InStop = (b & (1 << (3 - 1))) != 0;
}
#endregion Public Constructors
#region Public Properties
public bool InEmergenza { get; set; } = false;
public bool InMarcia { get; set; } = false;
public bool InStop { get; set; } = false;
#endregion Public Properties
#region Public Methods
public override bool Equals(object obj)
{
// Object is not a GaugeModel instance
if (!(obj is PlantStatus item))
return false;
if (InMarcia != item.InMarcia)
return false;
if (InEmergenza != item.InEmergenza)
return false;
if (InStop != item.InStop)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion Public Methods
}
}