using IOB_UT_NEXT; using MapoSDK; using Newtonsoft.Json; using S7.Net; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; using System.Threading; namespace IOB_WIN_NEXT { public class IobSiemens : IobGeneric, IDisposable { /* -------------------------------------------------------------------------------- * Controlli SIEMENS tramite libreria S7.net * - basasto su SIEMENS * - S7 testato vers 300 e 1200 * - attenzione conf rack/slot x varie serie controlli * - necessità apertura metodi pu/get * * -------------------------------------------------------------------------------- */ #region Protected Fields /// /// Oggetto PLC da ri-utilizzare... /// protected Plc currPLC; /// /// Ultimo controllo ping x evitare ping flood... /// protected DateTime lastPingConn = DateTime.Now.AddMinutes(-10); /// /// Esito ultimo ping /// protected bool lastPingOk = false; /// /// Lungh massima stringhe /// protected int maxStrChar = 20; /// /// parametri di connessione /// protected connParamS7 parametri; /// /// Oggetto cronometro x test vari... /// protected Stopwatch sw = new Stopwatch(); /// /// indica se scrivere i primi byte x string siemens x indicare lung max e corrente /// protected bool writePre = true; #endregion Protected Fields #region Public Fields /// /// Byte dimensione buffer dati memoria (da file map) /// public int numByte = 0; #endregion Public Fields #region Public Constructors /// /// Classe base con i metodi x Siemens /// /// /// public IobSiemens(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { memMap = new plcMemMap(); writePre = true; if (IOBConf.optPar.ContainsKey("WRITE_PRE")) { bool.TryParse(IOBConf.optPar["WRITE_PRE"], out writePre); lgInfo($"Override Write pre da conf: {IOBConf.optPar["WRITE_PRE"]} --> {writePre}"); } if (IOBConf != null) { // gestione invio ritardato contapezzi pzCountDelay = utils.CRI("pzCountDelay"); lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // inizializzo parametri... parametri = new connParamS7() { ipAdrr = "127.0.0.1", tipoCpu = CpuType.S7200, slot = 0, rack = 0, pingMsTimeout = IOBConf.pingMsTimeout, memAddrRead = "DB1.DBB0", memAddrWrite = "DB2.DBB0", memSizeRead = 0, memSizeWrite = 0 }; setParamPlc(); // salvo info su conf IOB... string iobConfSer = ""; try { iobConfSer = JsonConvert.SerializeObject(IOBConf, Formatting.Indented); } catch { } // finito! lgInfoStartup($"Init IOB, con {iobConfSer}"); } else { lgError("Impossibile avviare, IOBConf nullo/non valido!"); } } #endregion Public Constructors #region Protected Properties /// /// Dizionario delle ultime operazioni di scrittura per OGNI memoria (in modo che fa log ogni x sec...) /// protected Dictionary lastMemWrite { get; set; } = new Dictionary(); #endregion Protected Properties #region Private Methods private static int getScaledInt(dataConf currMem) { int valInt; // prima faccio eventuale fattore di scala... int.TryParse(currMem.value, out valInt); if (currMem.factor != 1) { valInt = (int)(valInt * currMem.factor); } return valInt; } private static uint getScaledUInt(dataConf currMem) { uint valUInt; // prima faccio eventuale fattore di scala... uint.TryParse(currMem.value, out valUInt); if (currMem.factor != 1) { valUInt = valUInt * (uint)currMem.factor; } return valUInt; } /// /// Verifica SE sia il caso di fare il log della memoria indicata /// /// /// private void maybeLogWrite(string memAddrWrite, string logValue) { bool doWrite = true; DateTime adesso = DateTime.Now; if (!lastMemWrite.ContainsKey(memAddrWrite)) { lastMemWrite.Add(memAddrWrite, adesso.AddMinutes(-1)); } // ora mi leggo valore ultima scrittura e confronto con adesso try { doWrite = (lastMemWrite[memAddrWrite].AddSeconds(vetoSeconds) < adesso); } catch (Exception exc) { lgError($"Eccezione in maybeLogWrite{Environment.NewLine}{exc}"); } // se encessario --> LOG! if (doWrite) { // 2022.03.16 portato a livello TRACE x evitare log troppo verboso lgTrace(logValue); lastMemWrite[memAddrWrite] = adesso; } } /// /// Effettua lettura del contatore e restitusice la stringa grezza del dato /// /// /// private string readCountVal(ref string memAddrPzCount) { object outputVal = new object(); string strOutVal; // cerco i pezzi contati STANDARD if (memAddrPzCount.StartsWith("STD")) { // inizio verifica area memoria/parametro levando prima parte codice memAddrPzCount = memAddrPzCount.Replace("STD.", ""); // verifico se si tratta di lettura area DB... formato tipo STD.DB700.DBB22.W if (memAddrPzCount.StartsWith("DB")) { memAreaSiemens areaCounter = new memAreaSiemens(memAddrPzCount); if (isVerboseLog) { lgInfo("[0] area memoria: {1}.{2}.{3}", memAddrPzCount, areaCounter.DbNum, areaCounter.indiceMem, areaCounter.tipoMem); } // copio da blocco già letto... con switch x tipo dati --> tipo lettura... e salvo ultimo conteggio rilevato switch (areaCounter.tipoMem) { case "B": byte valB = RawInput[areaCounter.indiceMem]; outputVal = valB; break; case "W": ushort valW = S7.Net.Types.Word.FromByteArray(RawInput.Skip(areaCounter.indiceMem).Take(2).ToArray()); outputVal = valW; break; case "DW": uint valDW = S7.Net.Types.DWord.FromByteArray(RawInput.Skip(areaCounter.indiceMem).Take(4).ToArray()); outputVal = valDW; break; case "DI": int valSDW = S7.Net.Types.DInt.FromByteArray(RawInput.Skip(areaCounter.indiceMem).Take(4).ToArray()); outputVal = valSDW; break; case "RE": double valRe = S7.Net.Types.Double.FromByteArray(RawInput.Skip(areaCounter.indiceMem).Take(4).ToArray()); outputVal = valRe; break; default: break; } } } else if (memAddrPzCount.StartsWith("SPEC")) { // inizio verifica area memoria/parametro levando prima parte codice memAddrPzCount = memAddrPzCount.Replace("SPEC.", ""); // verifico se si tratta di lettura area DB... formato tipo SPEC.DB700.DBB22.W if (memAddrPzCount.StartsWith("DB")) { memAreaSiemens areaCounter = new memAreaSiemens(memAddrPzCount); if (isVerboseLog) { lgInfo("[0] area memoria: {1}.{2}.{3}", memAddrPzCount, areaCounter.DbNum, areaCounter.indiceMem, areaCounter.tipoMem); } // leggo i dati SPECIFICI... byte[] MemBlockPZ = new byte[8]; bool fatto = false; // copio da blocco LEGGENDO con switch x tipo dati --> tipo lettura... e salvo ultimo conteggio rilevato switch (areaCounter.tipoMem) { case "B": MemBlockPZ = new byte[1]; fatto = S7ReadBB(ref MemBlockPZ, memAddrPzCount, 1); outputVal = MemBlockPZ[0]; break; case "W": MemBlockPZ = new byte[2]; fatto = S7ReadBB(ref MemBlockPZ, memAddrPzCount, 2); ushort valW = S7.Net.Types.Word.FromByteArray(MemBlockPZ.ToArray()); outputVal = valW; break; case "DW": MemBlockPZ = new byte[4]; fatto = S7ReadBB(ref MemBlockPZ, memAddrPzCount, 4); uint valDW = S7.Net.Types.DWord.FromByteArray(MemBlockPZ.ToArray()); outputVal = valDW; break; case "DI": MemBlockPZ = new byte[4]; fatto = S7ReadBB(ref MemBlockPZ, memAddrPzCount, 4); int valSDW = S7.Net.Types.DInt.FromByteArray(MemBlockPZ.ToArray()); outputVal = valSDW; break; case "RE": MemBlockPZ = new byte[4]; fatto = S7ReadBB(ref MemBlockPZ, memAddrPzCount, 4); double valRe = S7.Net.Types.Double.FromByteArray(MemBlockPZ.ToArray()); outputVal = valRe; break; default: break; } } } strOutVal = $"{outputVal}"; return strOutVal; } #endregion Private Methods #region Protected Methods /// /// decodifica da bitmap il CURRENT MODE del controllo /// /// /// protected virtual string decodeCurrMode(byte currModeBitmap) { string answ = ""; if (verboseLog) { lgInfo(string.Format("CURR_MODE raw data: {0}", utils.binaryForm(currModeBitmap))); } // decodifica del MODO... B21 /* * CURR MODE diviso in BIT: * B0 (01) = AUTO * B1 (02) = MDI * B2 (04) = JOG * B3 (08) = TeachIN (associato a MDI --> 10) * B4 (16) = Repos (associato a JOG --> 20) * B5 (32) = RefPoint (associato a Jog --> 36) * B6 (64) = Incr1 (associato a Jog --> 68) * B7 (128) = Incr10 (associato a Jog --> -124 / 132 se UInt) * */ // modi principali if (currModeBitmap.SelectBit(0)) { answ = "AUTO"; } else if (currModeBitmap.SelectBit(1)) { answ = "MDI"; } else if (currModeBitmap.SelectBit(2)) { answ = "JOG"; } // modi accessori if (currModeBitmap.SelectBit(3)) { answ += " | TEACH-IN"; } if (currModeBitmap.SelectBit(4)) { answ += " | REPOS"; } if (currModeBitmap.SelectBit(5)) { answ += " | REF-POINT"; } if (currModeBitmap.SelectBit(6)) { answ += " | INCR-1"; } if (currModeBitmap.SelectBit(7)) { answ += " | INCR-10"; } return answ; } /// /// Decodifica il resto dell'area x i dati accessori (allarmi, ...) /// protected virtual void decodeOtherData() { if (verboseLog) { } } /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO /// protected virtual void decodeToBaseBitmap() { // init a zero... B_input = 0; } /// /// Restituisce path completo file da chiave configurazione /// /// chiave conf x file richiesto /// protected string filePath(string keyFile) { string answ = ""; try { answ = $"{utils.confDir}\\{utils.CRS(keyFile)}"; } catch (Exception exc) { lgError(exc, "Eccezione in recupero filePath"); } return answ; } /// /// Override metodo x scrittura parametri su PLC /// /// protected override void plcWriteParams(ref List updatedPar) { dataConf currMem = null; int byteSize = 0; byte[] MemBlock = new byte[1]; string memAddrWrite = ""; bool fatto = false; string serObj = ""; if (updatedPar != null) { // controllo i parametri... ne gestisco 4... foreach (var item in updatedPar) { try { memAddrWrite = ""; int valInt = 0; uint valUInt = 0; // cerco in area memMapWrite... if (memMap.mMapWrite.ContainsKey(item.uid)) { // recupero! currMem = memMap.mMapWrite[item.uid]; byteSize = currMem.size; memAddrWrite = currMem.memAddr; MemBlock = new byte[byteSize]; // faccio preliminarmente upsertKey... upsertKey(currMem.name, currMem.value); serObj = JsonConvert.SerializeObject(item, Formatting.Indented); lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); serObj = JsonConvert.SerializeObject(currMem, Formatting.Indented); lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); switch (currMem.tipoMem) { case plcDataType.Boolean: break; case plcDataType.Int: valInt = getScaledInt(currMem); saveIntOnMemBlock(ref MemBlock, 0, valInt.ToString()); break; case plcDataType.DInt: valInt = getScaledInt(currMem); saveDIntOnMemBlock(ref MemBlock, 0, valInt.ToString()); break; case plcDataType.Word: valUInt = getScaledUInt(currMem); saveWordOnMemBlock(ref MemBlock, 0, valInt.ToString()); break; case plcDataType.DWord: valUInt = getScaledUInt(currMem); saveDWordOnMemBlock(ref MemBlock, 0, valInt.ToString()); break; case plcDataType.Real: saveRealOnMemBlock(ref MemBlock, 0, currMem.value); break; case plcDataType.String: // se ho writePre --> "allungo" di 2 la dimensione della stringa x MemBlock... if (writePre) { MemBlock = new byte[byteSize + 2]; } saveStringOnMemBlock(ref MemBlock, 0, currMem.size, currMem.value); break; default: break; } lgInfo($"Preparo scrittura{Environment.NewLine}---------------MemBlock data---------------{Environment.NewLine}{BitConverter.ToString(MemBlock)}{Environment.NewLine}--------------- END data ---------------"); if (!string.IsNullOrEmpty(memAddrWrite)) { // scrivo su siemens fatto = S7WriteBB(ref MemBlock, memAddrWrite); // se fatto --> aggiorno! if (fatto) { item.value = item.reqValue; item.reqValue = ""; item.lastRead = DateTime.Now; } // se configurato faccio verifica write... if (getOptPar("WRITE_CHECK") == "TRUE") { byte[] MemBlockRead = new byte[MemBlock.Length]; S7ReadBB(ref MemBlockRead, memAddrWrite, MemBlock.Length); // se non corrispondessero loggo! if (!MemBlock.SequenceEqual(MemBlockRead)) { lgError($"Errore: mancata corrispondenza tra dati scritti e letti:{Environment.NewLine}Write: {BitConverter.ToString(MemBlock)}{Environment.NewLine}read: {BitConverter.ToString(MemBlockRead)}"); } else { lgInfo($"Scrittura corretta: {BitConverter.ToString(MemBlockRead)}"); } } } else { lgInfo($"Errore: memAddrWrite vuoto!"); } } else { lgInfo($"Errore uid non trovato in area write memory: {item.uid}, ci sono {memMap.mMapWrite.Count} in area write"); } } catch (Exception exc) { lgError($"Eccezione in fase di plcWriteParams per item {item.uid} con valore {item.value}{Environment.NewLine}{exc}"); } } } } /// /// Imposto parametri PLC /// protected override void setParamPlc() { // Creo oggetto connessione NC parentForm.commPlcActive = true; lgInfoStartup("Start init Adapter SIEMENS all'IP {0} | CPU: {1} | R/S: {2}/{3} | --> IOB {4}", cIobConf.cncIpAddr, cIobConf.cpuType, cIobConf.rack, cIobConf.slot, cIobConf.codIOB); // SE è necessario refresh... if (needRefresh) { lgInfoStartup("Refreshing connection..."); if (parametri != null) { try { parametri.slot = cIobConf.slot; parametri.rack = cIobConf.rack; parametri.tipoCpu = (CpuType)Enum.Parse(typeof(CpuType), cIobConf.cpuType); parametri.ipAdrr = cIobConf.cncIpAddr; // leggo file init... lgInfoStartup("Reading ini file..."); IniFile fIni = new IniFile(cIobConf.iniFileName); // ora leggo valori speciali parametri.memAddrRead = fIni.ReadString("MEMORY", "ADDR_READ", ""); parametri.memAddrWrite = fIni.ReadString("MEMORY", "ADDR_WRITE", ""); parametri.memSizeRead = fIni.ReadInteger("MEMORY", "SIZE_READ", 0); parametri.memSizeWrite = fIni.ReadInteger("MEMORY", "SIZE_WRITE", 0); // salvo vettori memoria... lgInfoStartup("Set RawInput dimensions..."); RawInput = new byte[parametri.memSizeRead]; RawOutput = new byte[parametri.memSizeWrite]; // salvo parametri conn! lgInfoStartup(string.Format("Parametri memoria: memAddrRead: {0} | memAddrWrite: {1} | memSizeRead: {2} | memSizeWrite: {3}", parametri.memAddrRead, parametri.memAddrWrite, parametri.memSizeRead, parametri.memSizeWrite)); } catch (Exception exc) { lgError(exc, "Errore in parse parametri da IOBConf"); } // ora tento avvio PLC... SE PING OK... IPStatus esitoPing = testPingMachine; if (esitoPing == IPStatus.Success) { needRefresh = false; try { currPLC = new Plc(parametri.tipoCpu, parametri.ipAdrr, parametri.rack, parametri.slot); // disconnetto e connetto... if (isVerboseLog) { lgInfo("SIEMENS: tryDisconnect"); } tryDisconnect(); // lo ripeto x evitare che ci sia un loop... e tryConnect richiami la procedura corrente... needRefresh = false; lgInfoStartup("SIEMENS: tryConnect"); lastConnectTry = DateTime.Now; tryConnect(); lgInfoStartup("End init Adapter SIEMENS"); if (isVerboseLog) { lgInfo("S7+ CONNESSIONE AVVENUTA"); } } catch (Exception exc) { lgError(exc, "Errore in INIT PLC S7+"); } } else { lgError($"IOB SIEMENS | Errore in ping: esito {esitoPing}"); } parentForm.commPlcActive = false; // carico conf vettore memoria... loadMemConf(); bool enableByApp = utils.CRB("enableContapezzi"); bool enableByIob = (getOptPar("ENABLE_PZCOUNT") == "TRUE"); bool disableByIob = (getOptPar("DISABLE_PZCOUNT") == "TRUE"); if ((enableByApp || enableByIob) && !(disableByIob)) { lgDebug("SIEMENS: inizio gestione contapezzi"); try { // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con indicazione area) if (cIobConf.optPar.Count > 0 && !string.IsNullOrWhiteSpace(getOptPar("PZCOUNT_MODE"))) { if (getOptPar("PZCOUNT_MODE").StartsWith("STD")) { lgDebug("Init contapezzi SIEMENS: pzCntReload(true)"); pzCntReload(true); // refresh associazione Macchina - IOB sendM2IOB(); // per adesso imposto lettura PLC == contapezzi (poi farà vera lettura...) contapezziPLC = contapezziIOB; } else { contapezziIOB = 0; lgDebug("Contapezzi STD disabilitato: modalità {0}", getOptPar("PZCOUNT_MODE")); } } else { contapezziIOB = 0; lgDebug("Parametro mancante PZCOUNT_MODE"); } } catch (Exception exc) { lgError(exc, "Errore in contapezzi SIEMENS"); } } } else { lgError("Parametri null!"); } } } /// /// Test connessione CNC /// /// protected bool testCncConn() { bool answ = false; // riduco i controlli ping.. li faccio solo ogni 5 ping period se precedente positivo... DateTime adesso = DateTime.Now; if (lastPingOk && adesso.Subtract(lastPingConn).TotalMilliseconds < 5 * parametri.pingMsTimeout) { answ = lastPingOk; } else { IPStatus pingStatus = testPingMachine; // se non ok riprovo 1 volta dopo attesa if (pingStatus != IPStatus.Success) { Thread.Sleep(2 * cIobConf.pingMsTimeout); pingStatus = testPingMachine; } // se non passa ancora errore! if (pingStatus != IPStatus.Success) { lgError($"Errore in testCncConn | reply Status {pingStatus} | IP: {parametri.ipAdrr} | T.Out: {parametri.pingMsTimeout}ms"); } // se passa il ping faccio il resto... else { if (!currPLC.IsConnected) { currPLC.Open(); } if (!currPLC.IsAvailable) { lgError($"PLC Siemens NON disponibile:{currPLC.LastErrorCode} | {currPLC.LastErrorString}"); currPLC.ClearLastError(); } else { if (!currPLC.IsConnected) { lgError($"PLC Siemens NON connesso:{currPLC.LastErrorCode} | {currPLC.LastErrorString}"); currPLC.ClearLastError(); parentForm.updateComStats("NO connection"); } else { // tutto ok parentForm.updateComStats("Connection OK"); answ = true; } } } // salvo stato ping lastPingOk = answ; lastPingConn = adesso; } return answ; } #endregion Protected Methods #region Public Methods /// /// Converte direttamente un valore Int su un oggetto byte[4] /// /// valore da scrivere public byte[] dintToByte(string valore) { byte[] answ = new byte[4]; try { int valInt = 0; int.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.DInt.ToByteArray(valInt); int byteLen = 4; Buffer.BlockCopy(strByte, 0, answ, 0, byteLen); } catch (Exception exc) { lgError($"Errore in gestione scrittura DINT {valore} in byte{Environment.NewLine}{exc}"); } return answ; } /// /// Metodo dispose x il currPLC contenuto /// public void Dispose() { currPLC.Dispose(); } /// /// Converte direttamente un valore UInt32 su un oggetto byte[4] /// /// valore da scrivere public byte[] dwordToByte(string valore) { byte[] answ = new byte[4]; try { ushort valInt = 0; ushort.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.DWord.ToByteArray(valInt); int byteLen = 4; Buffer.BlockCopy(strByte, 0, answ, 0, byteLen); } catch (Exception exc) { lgError($"Errore in gestione scrittura DINT {valore} in byte{Environment.NewLine}{exc}"); } return answ; } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); if (utils.CRB("enableTSVC")) { // processing SOLO SE ho in memoria abbastanza dati... if (RawInput.Length < parametri.memSizeRead) { lgError($"Impossibile processare getDynData x Siemens PLC, vettore memoria troppo piccolo: {RawInput.Length} byte / {parametri.memSizeRead} byte presenti/richiesti)"); } else { try { // processo x ogni valore configurato... if (memMap.mMapRead.Count > 0) { // inizializzo i valori bool valBool = false; double valore = 0; string valString = ""; // procedo x ogni valore configurato...... foreach (var item in memMap.mMapRead) { // in primis DEVO determinare di quale TIPO di valore ho bisogno... switch (item.Value.tipoMem) { case plcDataType.Boolean: valBool = S7.Net.Types.Boolean.GetValue(RawInput[item.Value.index], item.Value.size); break; case plcDataType.Int: valore = ((double)S7.Net.Types.Int.FromByteArray(RawInput.Skip(item.Value.index).Take(item.Value.size).ToArray())) / item.Value.factor; saveValue(ref outVal, valore, item.Key); break; case plcDataType.DInt: valore = ((double)S7.Net.Types.DInt.FromByteArray(RawInput.Skip(item.Value.index).Take(item.Value.size).ToArray())) / item.Value.factor; saveValue(ref outVal, valore, item.Key); break; case plcDataType.Word: valore = ((double)S7.Net.Types.Word.FromByteArray(RawInput.Skip(item.Value.index).Take(item.Value.size).ToArray())) / item.Value.factor; saveValue(ref outVal, valore, item.Key); break; case plcDataType.DWord: valore = ((double)S7.Net.Types.DWord.FromByteArray(RawInput.Skip(item.Value.index).Take(item.Value.size).ToArray())) / item.Value.factor; saveValue(ref outVal, valore, item.Key); break; case plcDataType.Real: valore = S7.Net.Types.Double.FromByteArray(RawInput.Skip(item.Value.index).Take(item.Value.size).ToArray()) / item.Value.factor; saveValue(ref outVal, valore, item.Key); break; case plcDataType.String: valString = S7.Net.Types.String.FromByteArray(RawInput.Skip(item.Value.index).Take(item.Value.size).ToArray()); break; default: break; } } } else { lgInfo($"getDynData: {memMap.mMapRead.Count} record in mMapRead"); } } catch (Exception exc) { lgError(exc, "Errore in getDynData x Siemens PLC"); } } } else { lgInfo($"Non processo getDynData: enableTSVC = false"); } if (periodicLog || outVal.Count > 0) { lgInfo($"Esito getDynData: {outVal.Count} valori VALIDI in outVal"); } return outVal; } /// /// Recupero programma in lavorazione /// /// public override string getPrgName() { // valore non presente in vers default... se gestito fare override string prgName = ""; return prgName; } /// /// Recupero programma in lavorazione come Dictionary FANUC... /// - SYSINFO: (prima KEY globale) TUTTI i valori separati da # (x fare check modifica) /// - altre stringhe: ogni singolo parametro / valore /// /// public override Dictionary getSysInfo() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); return outVal; } /// /// Converte direttamente un valore Short su un oggetto byte[2] /// /// valore da scrivere public byte[] intToByte(string valore) { byte[] answ = new byte[2]; try { short valInt = 0; short.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.Int.ToByteArray(valInt); int byteLen = 2; Buffer.BlockCopy(strByte, 0, answ, 0, byteLen); } catch (Exception exc) { lgError($"Errore in gestione scrittura INT {valore} in byte{Environment.NewLine}{exc}"); } return answ; } /// /// Effettua vero processing contapezzi /// public override void processContapezzi() { if (utils.CRB("enableContapezzi")) { try { // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con indicazione area) if (cIobConf.optPar.Count > 0) { int pzCountBuoni = -1; int pzCountQCtrl = -1; int pzCountAll = 0; string memAddrPzCount = ""; string strOutValPzBuoni = ""; string memAddrPzQCtrl = ""; string strOutValPzQCtrl = ""; if (!string.IsNullOrEmpty(getOptPar("PZCOUNT_MODE"))) { memAddrPzCount = getOptPar("PZCOUNT_MODE"); strOutValPzBuoni = readCountVal(ref memAddrPzCount); // verifico valori letti if (!string.IsNullOrEmpty(strOutValPzBuoni)) { // salvo... Int32.TryParse(strOutValPzBuoni, out pzCountBuoni); // e sommo (SE sono > -1) if (pzCountBuoni >= 0) { pzCountAll += pzCountBuoni; } } } if (!string.IsNullOrEmpty(getOptPar("PZQCTRL_MODE"))) { memAddrPzQCtrl = getOptPar("PZQCTRL_MODE"); strOutValPzQCtrl = readCountVal(ref memAddrPzQCtrl); // verifico valori letti if (!string.IsNullOrEmpty(strOutValPzQCtrl)) { // salvo... Int32.TryParse(strOutValPzQCtrl, out pzCountQCtrl); // e sommo (SE sono > -1) if (pzCountQCtrl >= 0) { pzCountAll += pzCountQCtrl; } } } // proseguo SOLO SE ho un pzCount > 0... if (pzCountAll >= 0) { // verifico il NUOVO contapezzi PRIMA di accettare ogni incremento... int deltaPzCount = pzCountAll - contapezziPLC; double maxDelta = DateTime.Now.Subtract(plcLastPzRead).TotalMinutes / (plcAvgTc / 60); // se incremento superiore del doppio atteso --> segnalo errore e NON accetto if (deltaPzCount > maxDelta * (maxPzDeltaPerc / 100)) { lgError($"[DELTA CHECK]: intremento contapezziPLC troppo elevato: lettura {pzCountAll} | contapezzi attuale: {contapezziPLC} | ultima lettura PLC: {plcLastPzRead} | TCiclo medio: {plcAvgTc}s | incremento accettato "); } else { contapezziPLC = (pzCountAll >= 0 ? pzCountAll : contapezziPLC); if (isVerboseLog) { lgInfo($"[2] pzBuoni: {strOutValPzBuoni} | pzCQ: {strOutValPzQCtrl}"); lgInfo($"[3] contapezziPLC: {contapezziPLC}"); } } } } } catch (Exception exc) { lgError(exc, "Errore in contapezzi SIEMENS"); } } } /// /// Esegue processing MODE (e nel contempo recupera altri dati dell'area G) /// public override void processMode() { // valore non presente in vers default... se gestito fare override if (utils.CRB("enableMode")) { } } /// /// Effettua lettura semafori principale /// Parametri da aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { base.readSemafori(ref currDispData); try { currDispData.semIn = Semaforo.SV; if (verboseLog) { lgInfo("inizio read semafori"); } // leggo TUTTI i byte configurati... byte[] MemBlock = new byte[parametri.memSizeRead]; bool fatto = S7ReadBB(ref MemBlock); Buffer.BlockCopy(MemBlock, 0, RawInput, 0, parametri.memSizeRead); if (verboseLog) { lgInfo(string.Format("RawInput[0]: {0}", utils.binaryForm(RawInput[0]))); } // salvo il solo BYTE dell'input decifrando il semaforo... decodeToBaseBitmap(); decodeOtherData(); // riporto bitmap... reportRawInput(ref currDispData); } catch { currDispData.semIn = Semaforo.SR; } } /// /// wrapper chiamata LETTURA in blocco MULTI BYTE dell'area read DI DEFAULT... /// /// /// public bool S7ReadBB(ref byte[] Value) { return S7ReadBB(ref Value, parametri.memAddrRead, parametri.memSizeRead); } /// /// wrapper chiamata LETTURA in blocco MULTI BYTE... /// /// MATRICE valori letti /// Area memoria da leggere... /// Numero byte da leggere /// public bool S7ReadBB(ref byte[] Value, string memAddrRead, int numByte) { bool answ = false; if (Value != null) { sw.Restart(); parentForm.commPlcActive = true; if (testCncConn()) { // decodifico memoria... memAreaSiemens memoria = new memAreaSiemens(memAddrRead); Byte[] memByteRead = currPLC.ReadBytes(DataType.DataBlock, memoria.DbNum, memoria.indiceMem, numByte); // copio in value, sennò do errore... if (memByteRead.Length == Value.Length) { Value = memByteRead; } else { lgError($"Mismatch dimensione array memoria: indirizzo: {memAddrRead} | passato array di {Value.Length} byte, letti da S7 {memByteRead.Length} byte"); numErroriCheck++; } string titolo = $"READ BLOCK MEM BYTE: {parametri.memAddrRead} --> {numByte} byte"; if (verboseLog) { lgInfo(titolo); } string contenuto = $"Contenuto area memoria acquisita{Environment.NewLine}"; string byteVal = ""; for (int i = 0; i < memByteRead.Length; i++) { byteVal = Convert.ToString(memByteRead[i], 2).PadLeft(8, '0'); contenuto += string.Format("B{0:000}: {1} | {2}{3}", i, byteVal, memByteRead[i], Environment.NewLine); } // loggo lettura... if (verboseLog) { lgInfo(contenuto); } } else { connectionOk = false; } parentForm.commPlcActive = false; sw.Stop(); if (utils.CRB("recTime")) { TimingData.addResult(cIobConf.codIOB, string.Format("{0}|{1}", parametri.memAddrRead, numByte), sw.ElapsedTicks); } } // se supero soglia errori lettura --> disconnetto e resetto if (numErroriCheck > maxErroriCheck) { lgInfo($"numErroriCheck: {numErroriCheck} --> richiesta disconnessione adapter con tryDisconnect"); numErroriCheck = 0; tryDisconnect(); } return answ; } /// /// wrapper chiamata LETTURA in blocco MULTI BYTE... default size a parametri.memSizeRead /// /// /// Area memoria da leggere... /// public bool S7ReadBB(ref byte[] Value, string memAddrRead) { bool answ = false; answ = S7ReadBB(ref Value, memAddrRead, parametri.memSizeRead); return answ; } /// /// wrapper chiamata SCRITTURA in blocco MULTI BYTE, DI DEFAULT su area configurata x scrittura CONTINUA... /// /// /// public bool S7WriteBB(ref byte[] Value) { return S7WriteBB(ref Value, parametri.memAddrWrite); } /// /// Override scrittura in area DBB /// /// /// /// public bool S7WriteBB(ref byte[] Value, string memAddrWrite) { bool answ = false; if (Value == null) { lgError($"Errore in S7WriteBB: Value è null"); } else { if (string.IsNullOrEmpty(memAddrWrite)) { lgError($"Errore in S7WriteBB: memAddrWrite è vuoto"); } else { sw.Restart(); if (testCncConn()) { try { // decodifico memoria... memAreaSiemens memoria = new memAreaSiemens(memAddrWrite); int numByte = Value.Length; ErrorCode errorCode = currPLC.WriteBytes(DataType.DataBlock, memoria.DbNum, memoria.indiceMem, Value); switch (errorCode) { case ErrorCode.NoError: answ = true; maybeLogWrite(memAddrWrite, $"S7WriteBB-01 Effettuata correttamente scrittura su PLC: MEMORIA {memAddrWrite} | numByte: {Value.Length} | ValOriginale: {BitConverter.ToString(Value)}"); break; case ErrorCode.WrongCPU_Type: case ErrorCode.ConnectionError: case ErrorCode.IPAddressNotAvailable: case ErrorCode.WrongVarFormat: case ErrorCode.WrongNumberReceivedBytes: case ErrorCode.SendData: case ErrorCode.ReadData: case ErrorCode.WriteData: lgError($"Errore in S7WriteBB su {memAddrWrite}: {errorCode.ToString()} | numByte: {Value.Length}| {Value.ValToBinString()}"); answ = false; break; default: break; } } catch (Exception exc) { lgError($"Eccezione in S7WriteBB | memAddrWrite {memAddrWrite} | numByte: {Value.Length}{Environment.NewLine}{exc}"); } } sw.Stop(); } } return answ; } /// /// Override scrittura in area DBB /// /// Valore byte[] da scrivere /// Numero del DB (es 700 per DB700) /// Indice interno al datablock del byte da cui partire /// public bool S7WriteBB(ref byte[] Value, int DbNum, int IndiceMem) { bool answ = false; if (Value == null) { lgError($"Errore in S7WriteBB: Value è null"); } else { if (DbNum < 0 || IndiceMem < 0) { lgError($"Errore in S7WriteBB | DbNum: {DbNum} | IndiceMem: {IndiceMem}"); } else { sw.Restart(); if (testCncConn()) { try { int numByte = Value.Length; ErrorCode errorCode = currPLC.WriteBytes(DataType.DataBlock, DbNum, IndiceMem, Value); switch (errorCode) { case ErrorCode.NoError: lgInfo($"S7WriteBB-02 Effettuata correttamente scrittura su PLC: DB {DbNum}.{IndiceMem} | numByte: {Value.Length}| {Value.ValToBinString()}"); break; case ErrorCode.WrongCPU_Type: case ErrorCode.ConnectionError: case ErrorCode.IPAddressNotAvailable: case ErrorCode.WrongVarFormat: case ErrorCode.WrongNumberReceivedBytes: case ErrorCode.SendData: case ErrorCode.ReadData: case ErrorCode.WriteData: lgError($"Errore in S7WriteBB su DB {DbNum}.{IndiceMem}: {errorCode.ToString()} | numByte: {Value.Length}| {Value.ValToBinString()}"); break; default: break; } answ = true; } catch (Exception exc) { lgError($"Eccezione in S7WriteBB: DbNum {DbNum}, IndiceMem: {IndiceMem}, numByte: {Value.Length}{Environment.NewLine}{exc}"); } } sw.Stop(); } } return answ; } /// /// Salvo in memblock il valore DInt indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Posizione inizio scrittura /// Valore da scrivere public void saveDIntOnMemBlock(ref byte[] MemBlock, int startPos, string valore) { try { int valInt = 0; int.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.DInt.ToByteArray(valInt); int byteLen = 4; Buffer.BlockCopy(strByte, 0, MemBlock, startPos, byteLen); //var verifica = S7.Net.Types.String.FromByteArray(MemBlock); } catch (Exception exc) { lgError($"Errore in gestione scrittura DINT {valore} alla posizione {startPos} byte{Environment.NewLine}{exc}"); } } /// /// Salvo in memblock il valore DInt indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Valore da scrivere /// Posizione inizio scrittura public void saveDIntOnMemBlock(ref byte[] MemBlock, string stringKey, int startPos) { if (currProdData.ContainsKey(stringKey)) { try { string valore = currProdData[stringKey]; saveDIntOnMemBlock(ref MemBlock, startPos, valore); } catch (Exception exc) { lgError($"Errore in gestione scrittura DINT {stringKey}{Environment.NewLine}{exc}"); } } } /// /// Salvo in memblock il valore DInt indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Posizione inizio scrittura /// Valore da scrivere public void saveDWordOnMemBlock(ref byte[] MemBlock, int startPos, string valore) { try { byte[] stringPar = new byte[4]; int valInt = 0; int.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.DInt.ToByteArray(valInt); int byteLen = 4; Buffer.BlockCopy(strByte, 0, MemBlock, startPos, byteLen); //var verifica = S7.Net.Types.String.FromByteArray(MemBlock); } catch (Exception exc) { lgError($"Errore in gestione scrittura DWord {valore} alla posizione {startPos} byte{Environment.NewLine}{exc}"); } } /// /// Salvo in memblock il valore DInt indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Valore da scrivere /// Posizione inizio scrittura public void saveDWordOnMemBlock(ref byte[] MemBlock, string stringKey, int startPos) { if (currProdData.ContainsKey(stringKey)) { try { string valore = currProdData[stringKey]; saveDIntOnMemBlock(ref MemBlock, startPos, valore); } catch (Exception exc) { lgError($"Errore in gestione scrittura DINT {stringKey}{Environment.NewLine}{exc}"); } } } /// /// Salvo in memblock il valore Int indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Posizione inizio scrittura /// valore da scrivere public void saveIntOnMemBlock(ref byte[] MemBlock, int startPos, string valore) { try { short valInt = 0; short.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.Int.ToByteArray(valInt); int byteLen = 2; Buffer.BlockCopy(strByte, 0, MemBlock, startPos, byteLen); //var verifica = S7.Net.Types.String.FromByteArray(MemBlock); } catch (Exception exc) { lgError($"Errore in gestione scrittura INT {valore} alla posizione {startPos} byte{Environment.NewLine}{exc}"); } } /// /// Salvo in memblock il valore Int indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Nome del parametro da recuperare da prodData x scrivere /// Posizione inizio scrittura public void saveIntOnMemBlock(ref byte[] MemBlock, string stringKey, int startPos) { if (currProdData.ContainsKey(stringKey)) { try { string valore = currProdData[stringKey]; saveIntOnMemBlock(ref MemBlock, startPos, valore); } catch (Exception exc) { lgError($"Errore in gestione scrittura INT {stringKey}{Environment.NewLine}{exc}"); } } } /// /// Salvo in memblock il valore stringa indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Posizione inizio scrittura /// Valore scrivere public void saveRealOnMemBlock(ref byte[] MemBlock, int startPos, string valore) { try { byte[] stringPar = new byte[2]; double valReal = 0; double.TryParse(valore, out valReal); byte[] strByte = S7.Net.Types.Double.ToByteArray(valReal); int byteLen = 4; Buffer.BlockCopy(strByte, 0, MemBlock, startPos, byteLen); //var verifica = S7.Net.Types.String.FromByteArray(MemBlock); } catch (Exception exc) { lgError($"Errore in gestione scrittura REAL {valore} alla posizione {startPos} byte{Environment.NewLine}{exc}"); } } /// /// Salvo in memblock il valore stringa indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Valore scrivere /// Posizione inizio scrittura public void saveRealOnMemBlock(ref byte[] MemBlock, string stringKey, int startPos) { if (currProdData.ContainsKey(stringKey)) { try { string valore = currProdData[stringKey]; saveRealOnMemBlock(ref MemBlock, startPos, valore); } catch (Exception exc) { lgError($"Errore in gestione scrittura REAL {stringKey}{Environment.NewLine}{exc}"); } } } /// /// Salvo in memblock il valore stringa indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Posizione inizio scrittura /// Lunghezza max stringa (se ci sono 2 byte iniziali verrà ridotta di 2) /// valore da scrivere public void saveStringOnMemBlock(ref byte[] MemBlock, int startPos, int totLen, string valore) { if (MemBlock != null) { // loggare valore? fornire un output con memBlock e NON ref? try { lgInfo($"saveStringOnMemBlock: MemBlock size: {MemBlock.Length} | startPos: {startPos} | totLen: {totLen} | valore: {valore}"); byte[] stringPar = new byte[2]; byte[] strByte = S7.Net.Types.String.ToByteArray(valore); int byteLen = strByte.Length <= totLen ? strByte.Length : totLen; int shiftStrByte = writePre ? 2 : 0; if (writePre) { // MAX LUN stringPar[1] = (byte)totLen; // LUNGH STRING stringPar[0] = (byte)byteLen; Buffer.BlockCopy(stringPar, 0, MemBlock, startPos, shiftStrByte); } Buffer.BlockCopy(strByte, 0, MemBlock, startPos + shiftStrByte, byteLen); } catch (Exception exc) { lgError($"Errore in gestione scrittura {valore} alla posizione {startPos} per {totLen} byte{Environment.NewLine}{exc}"); } } else { lgError("Errore: MemBlock nullo"); } } /// /// Salvo in memblock il valore stringa indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Nome del parametro da recuperare da prodData x scrivere /// Posizione inizio scrittura /// Lunghezza max stringa (se ci sono 2 byte iniziali verrà ridotta di 2) public void saveStringOnMemBlock(ref byte[] MemBlock, string stringKey, int startPos, int totLen) { if (currProdData.ContainsKey(stringKey)) { try { string valore = currProdData[stringKey]; saveStringOnMemBlock(ref MemBlock, startPos, totLen, valore); } catch (Exception exc) { lgError($"Errore in gestione scrittura x key {stringKey}{Environment.NewLine}{exc}"); } } } /// /// Salvo in memblock il valore DInt indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Posizione inizio scrittura /// Valore da scrivere public void saveWordOnMemBlock(ref byte[] MemBlock, int startPos, string valore) { try { byte[] stringPar = new byte[4]; int valInt = 0; int.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.DInt.ToByteArray(valInt); int byteLen = 4; Buffer.BlockCopy(strByte, 0, MemBlock, startPos, byteLen); //var verifica = S7.Net.Types.String.FromByteArray(MemBlock); } catch (Exception exc) { lgError($"Errore in gestione scrittura Word {valore} alla posizione {startPos} byte{Environment.NewLine}{exc}"); } } /// /// Salvo in memblock il valore DInt indicato con formattazione siemens /// /// Blocco memoria come byte[] dove scrivere /// Valore da scrivere /// Posizione inizio scrittura public void saveWordOnMemBlock(ref byte[] MemBlock, string stringKey, int startPos) { if (currProdData.ContainsKey(stringKey)) { try { string valore = currProdData[stringKey]; saveDIntOnMemBlock(ref MemBlock, startPos, valore); } catch (Exception exc) { lgError($"Errore in gestione scrittura DINT {stringKey}{Environment.NewLine}{exc}"); } } } /// /// Converte direttamente un valore stringa su un oggetto byte[] (senza limitazioni di dimensione) /// /// /// Dimensione massima ammessa per la stringa /// public byte[] stringToByte(string valore, int maxLenght) { byte[] answ = new byte[1]; byte[] stringPar = new byte[2]; byte[] strByte = S7.Net.Types.String.ToByteArray(valore); int shiftStrByte = writePre ? 2 : 0; int byteLen = strByte.Length <= maxLenght ? strByte.Length : maxLenght; if (writePre) { // MAX LUN stringPar[1] = (byte)maxLenght; // LUNGH STRING stringPar[0] = (byte)byteLen; Buffer.BlockCopy(stringPar, 0, answ, 0, shiftStrByte); } Buffer.BlockCopy(strByte, 0, answ, shiftStrByte, byteLen); return answ; } /// /// Override connessione /// public override void tryConnect() { bool doLog = (verboseLog || periodicLog); lgInfoStartup("SIEMENS: tryConnect step 01"); if (!connectionOk) { // SE è necessario refresh... if (needRefresh) { lgInfoStartup("SIEMENS: tryConnect step 02"); // reimporto parametri PLC se necessario... setParamPlc(); } lgInfoStartup("SIEMENS: tryConnect step 03"); // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (doLog) { lgInfoStartup("SIEMENS: ConnKO - tryConnect"); } lgInfoStartup("SIEMENS: tryConnect step 04"); // in primis salvo data ping... lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { string szStatusConnection = "ND"; try { // ora provo connessione... parentForm.commPlcActive = true; currPLC.Open(); szStatusConnection = "OPEN"; parentForm.commPlcActive = false; connectionOk = true; lgInfoStartup($"StatusConnection: {szStatusConnection}"); // refresh stato allarmi!!! if (connectionOk) { if (adpRunning) { lgInfo($"Connessione OK: {connectionOk} | adpRunning: {adpRunning}"); } } else { lgError("Impossibile procedere, connessione mancante..."); } } catch (Exception exc) { lgFatal($"Errore in TryConnect adapter SIEMENS | szStatusConnection {szStatusConnection}{Environment.NewLine}{exc}"); connectionOk = false; needRefresh = true; } } else { // loggo no risposta ping ... connectionOk = false; if (doLog) { lgInfo($"Attenzione: SIEMENS controllo PING fallito per IP {cIobConf.cncIpAddr}"); } } } } // se non è ancora connesso faccio procesisng memoria caso disconnesso... if (!connectionOk) { // processo semafori ed invio... processMemoryDiscon(); } } /// /// Override disconnessione /// public override void tryDisconnect() { if (connectionOk) { string szStatusConnection = ""; try { currPLC.Close(); connectionOk = false; lgInfo(szStatusConnection); lgInfo("Effettuata disconnessione adapter SIEMENS!"); } catch (Exception exc) { lgFatal(exc, "Errore nella disconnessione dall'adapter SIEMENS"); } } else { lgError("IMPOSSIBILE effettuare disconnessione SIEMENS: Connessione non disponibile..."); } } /// /// Converte direttamente un valore UInt16 su un oggetto byte[2] /// /// valore da scrivere public byte[] wordToByte(string valore) { byte[] answ = new byte[2]; try { ushort valInt = 0; ushort.TryParse(valore, out valInt); byte[] strByte = S7.Net.Types.Word.ToByteArray(valInt); int byteLen = 2; Buffer.BlockCopy(strByte, 0, answ, 0, byteLen); } catch (Exception exc) { lgError($"Errore in gestione scrittura INT {valore} in byte{Environment.NewLine}{exc}"); } return answ; } #endregion Public Methods } }