using MapoSDK; using System; using System.Collections.Generic; using System.Linq; namespace IOB_WIN_NEXT { /// /// Controllo Siemens specifico x impianti SAET /// public class IobSiemensSaet : IobSiemens { /* -------------------------------------------------------------------------------- * Controlli SIEMENS SAET (impianti ad induzione in VALVITAL) * - basasto su SIEMENS * - S7 vers 1500 * * STRUTTURA MEMORIA DB1275: primi 92 byte lettura, poi 56 byte scrittura, vedere doc allegato * G:\Drive condivisi\30_Clienti\Valvital\Comunicazione PLC\SAET (forno e tempra) * * Si intende tutto con DB1275.DBxx * * - parametri processo * - DBD00: Watchdog INT SAET Alive ( 1-9999 ) * * - DB60.DBD6: pressione camera filtrante (salvataggio del MAX ogni minuto) | var testVal = S7.Net.Types.Double.FromByteArray(memByteRead.Skip(0).Take(4).ToArray()); * - DB60.DBD10: pressione linea utenze (salvataggio del MAX ogni minuto) * - DB60.DBD14: temperatura acqua pulita (salvataggio del MAX ogni minuto) * * - BIT di stato * - DBX2.1: READY TO RUN in AUTOMATICO * - DBX2.3: Macchina in LAVORAZIONE * - DBX2.4: WARNING Differenza tra Part Code MES - Saet (blu) * - DBX2.5: se 1 --> LAMPADA ROSSA (allarmi almeno 1 attivo) * * PartCounter DINT 4.0 Conteggio Parziale di pezzi "OK" prodotti dalla macchina * NumberCode String [12] 8.0 Valore numerico associato alla ricetta di produzione attualmente utilizzata dalla macchina * NewCode INT 22.0 "1= Avvenuta ricezione del segnale ""richiesta nuovo ordine di produzione (NEW CODE)"" * ricevuto dal server,impostabile su 1 solo quando la macchina NON è in produzione attiva" * Potenza utilizzata ST.1 REAL 24 Potenza utilizzata dalla stazione di riscaldo 1 [kW] * Potenza utilizzata ST.2 REAL 28 Potenza utilizzata dalla stazione di riscaldo 2 [kW] * Potenza utilizzata ST.3 REAL 32 Potenza utilizzata dalla stazione di riscaldo 3 [kW] * Potenza utilizzata ST.4 REAL 36 Potenza utilizzata dalla stazione di riscaldo 4 [kW] * Lettura Pirometro ST.1 REAL 40 Lettura Pirometro della stazione di riscaldo 1 [°C] * Lettura Pirometro ST.2 REAL 44 Lettura Pirometro della stazione di riscaldo 2 [°C] * Lettura Pirometro ST.3 REAL 48 Lettura Pirometro della stazione di riscaldo 3 [°C] * Lettura Pirometro ST.4 REAL 52 Lettura Pirometro della stazione di riscaldo 4 [°C] * Temperatura Acqua Raff Conv. ST.1 REAL 56 Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 1 [°C] * Temperatura Acqua Raff Conv. ST.2 REAL 60 Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 2 [°C] * Temperatura Acqua Raff Conv. ST.3 REAL 64 Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 3 [°C] * Temperatura Acqua Raff Conv. ST.4 REAL 68 Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 4 [°C] * Part_Status ST.1 INT 72 Stato Pezzo Stazione di Riscaldo 1 (0=Assente 1=Grezzo 10=OK 11=NOK) * Part_Status ST.2 INT 74 Stato Pezzo Stazione di Riscaldo 2 (0=Assente 1=Grezzo 10=OK 11=NOK) * Part_Status ST.3 INT 76 Stato Pezzo Stazione di Riscaldo 3 (0=Assente 1=Grezzo 10=OK 11=NOK) * Part_Status ST.4 INT 78 Stato Pezzo Stazione di Riscaldo 4 (0=Assente 1=Grezzo 10=OK 11=NOK) * Reserve_12 REAL 80 Riserva * Reserve_13 REAL 84 Riserva * Reserve_14 REAL 88 Riserva * -------------------------------------------------------------------------------- */ #region Protected Fields protected int counterMes2Plc = 0; protected int counterPlc2Mes = 0; protected int counterPlc2MesWrote = 0; protected DateTime lastPLCWatchDog; #endregion Protected Fields #region Public Constructors /// /// Classe base con i metodi x Siemens /// /// /// public IobSiemensSaet(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { lgInfo("NEW IOB SIEMENS versione SAET"); lastPLCWatchDog = DateTime.Now.AddMinutes(-1); } #endregion Public Constructors #region Protected Methods /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO /// protected override void decodeToBaseBitmap() { // init a zero... B_input = 0; /* ----------------------------------------------------- * bitmap MAPO STANDARD * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * B4: manuale * B5: anomalia * * * - BIT di stato * - DBX2.0: macchina accesa * - DBX2.1: CICLO AUTO = NON HO ANOMALIE/ALLARMI (0 --> allarme) * - DBX2.2: contapezzi * - DBX2.3: MACCHINA IN CICLO AUTO = LAVORA * - DBX2.4: ERRORE tra part code MES/SAET (blu) * - DBX2.5: Anomalia presente (rosso) ----------------------------------------------------- */ byte mainData = RawInput[2]; int byteSignals = 0; // bit 0 (poweron) imposto a 1 SE connected... if (currPLC.IsConnected) { byteSignals += (1 << 0); } if ((mainData & (1 << 3)) != 0) { byteSignals += (1 << 1); } // controllo il bit ALLARME if ((mainData & (1 << 1)) == 0) { byteSignals += (1 << 3); } // controllo il bit ANOMALIA if ((mainData & (1 << 5)) != 0) { byteSignals += (1 << 3); } // considero NON auto come manuale... if ((mainData & (1 << 3)) == 0) { byteSignals += (1 << 4); } // salvo! B_input = byteSignals; // processo il watchdog! counterPlc2Mes = S7.Net.Types.Int.FromByteArray(RawInput.Skip(0).Take(2).ToArray()); // ogni 60 registro... if (Math.Abs(counterPlc2Mes - counterPlc2MesWrote) > 60) { lgInfo($"WatchDog da PLC: {counterPlc2Mes}"); counterPlc2MesWrote = counterPlc2Mes; } // log opzionale! if (verboseLog) { lgInfo($"Trasformazione dati: RawInput:{RawInput[3]} --> B_input: {B_input}"); } } #endregion Protected Methods #region Public Methods /// /// Processo i task richiesti e li elimino dalla coda 1:1 /// /// public override Dictionary executeTasks(Dictionary task2exe) { // Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti... Dictionary taskDone = new Dictionary(); bool taskOk = false; string taskVal = ""; // verifico non sia null if (task2exe != null) { // inizio con 1 byte di default byte[] MemBlock = new byte[1]; string memAddrWrite = ""; // cerco task specifici 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.nihil: case taskType.forceResetPzCount: case taskType.forceSetPzCount: case taskType.setProg: taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; break; case taskType.setArt: saveProdData(item); MemBlock = new byte[34]; saveStringOnMemBlock(ref MemBlock, "setArt", 0, 32); memAddrWrite = "DB1275.DBB96"; taskVal = item.Value; break; case taskType.setComm: saveProdData(item); MemBlock = new byte[14]; saveStringOnMemBlock(ref MemBlock, "setComm", 0, 12); memAddrWrite = "DB1275.DBB130"; taskVal = item.Value; break; case taskType.setPzComm: // imposto i valori... MemBlock = new byte[4]; int numPz = 0; int.TryParse(item.Value, out numPz); MemBlock = S7.Net.Types.DInt.ToByteArray(numPz); memAddrWrite = "DB1275.DBB144"; break; case taskType.setParameter: // richiedo da URL i parametri WRITE da popolare lgInfo("Chiamata 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; case taskType.sendWatchDogMes2Plc: MemBlock = new byte[2]; MemBlock[1] = (byte)counterMes2Plc; MemBlock[0] = (byte)(counterMes2Plc >> 8); memAddrWrite = "DB1275.DBB92"; taskVal = $"VALUE DB1275.92 --> {counterMes2Plc}"; break; case taskType.endProd: case taskType.startSetup: // processo scrittura BIT x richiesta nuovo ordine a INIZIO setup --> è DINT! MemBlock = new byte[2]; MemBlock[0] = (byte)1; MemBlock[1] = (byte)1; memAddrWrite = "DB1275.DBB94"; lgInfo($"Chiamata startSetup | memAddrWrite: {memAddrWrite} | MemBlock: {MemBlock}"); break; case taskType.fixStopSetup: case taskType.stopSetup: // processo scrittura BIT x richiesta nuovo ordine a FINE setup MemBlock = new byte[2]; MemBlock[0] = (byte)0; MemBlock[1] = (byte)0; memAddrWrite = "DB1275.DBB94"; lgInfo($"Chiamata stopSetup | memAddrWrite: {memAddrWrite} | MemBlock: {MemBlock}"); break; default: taskVal = "SKIPPED | NO EXEC"; break; } // verifico se posso aggiornare valori in memoria... 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}"; } // aggiungo task! taskDone.Add(item.Key, taskVal); // scrivo comunque! taskOk = S7WriteBB(ref MemBlock, memAddrWrite); if (!taskOk) { lgError($"Errore in S7WriteBB durante executeTasks: {item.Key} | {item.Value}"); } } } return taskDone; } /// /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid) /// public override void processOverride() { } public override void processWhatchDog() { // scrivo 1 volta ogni n secondi il contatore incrementale su area apposita DateTime adesso = DateTime.Now; if (adesso.Subtract(lastPLCWatchDog).TotalSeconds > watchDogPeriod) { // incremento counterMes2Plc++; // se > 9999 --> 0 if (counterMes2Plc > 9999) counterMes2Plc = 0; // salvo su DB Dictionary task2exe = new Dictionary(); Dictionary taskDone = new Dictionary(); task2exe.Add("sendWatchDogMes2Plc", counterMes2Plc.ToString()); taskDone = executeTasks(task2exe); // salvo watchdog PLC lastPLCWatchDog = adesso; } } #endregion Public Methods } }