using EasyModbus; using IOB_UT_NEXT; using MapoSDK; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; namespace IOB_WIN_NEXT { /* -------------------------------------------------------------------------------- * Controlli ModBusTCP COMECA * - protocollo ModBus TCP * * -------------------------------------------------------------------------------- */ public class IobModbusTCP : IobGeneric { #region Private Fields /// /// Valore delta x gestione min/MAX e valore rilevato /// private double deltaVal = 0; #endregion Private Fields #region Protected Fields protected ModbusClient currPLC; /// /// Ultimo controllo ping x evitare ping flood... /// protected DateTime lastPingConn = DateTime.Now.AddMinutes(-10); /// /// Esito ultimo ping /// protected bool lastPingOk = false; /// /// Setup blocchi memorie read (indirizzo inizio, size) /// protected Dictionary memSetR = new Dictionary(); /// /// Setup blocchi memorie write (indirizzo inizio, size) /// protected Dictionary memSetW = new Dictionary(); /// /// parametri di connessione /// protected connParamModBusTCP parametri; /// /// Oggetto cronometro x test vari... /// protected Stopwatch sw = new Stopwatch(); #endregion Protected Fields #region Public Constructors /// Classe base con i metodi x ModBusTCP /// /// /// public IobModbusTCP(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { lgInfo("NEW IOB ModBus TCP"); setupMemBlocks(); memMap = new plcMemMap(); if (IOBConf != null) { // gestione invio ritardato contapezzi pzCountDelay = utils.CRI("pzCountDelay"); lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // inizializzo parametri... parametri = new connParamModBusTCP() { ipAdrr = "127.0.0.1", port = 502, pingMsTimeout = IOBConf.pingMsTimeout, memAddrRead = "40001", memAddrWrite = "41001", memSizeRead = 0, memSizeWrite = 0 }; setParamPlc(); // salvo info su conf IOB... string iobConfSer = ""; try { iobConfSer = JsonConvert.SerializeObject(IOBConf, Formatting.Indented); } catch { } // finito! lgInfo($"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 /// /// converte valore in tipo desiderato /// /// /// /// /// private static double convertVal(int[] listInt, int size, plcDataType tipoMem) { double valore = 0; //verifico se sia INT o real x convertire... switch (tipoMem) { case plcDataType.Real: if (size == 4) { valore = ModbusClient.ConvertRegistersToDouble(listInt); } else if (size == 2) { valore = ModbusClient.ConvertRegistersToFloat(listInt); } break; case plcDataType.Int: default: if (size == 4) { valore = ModbusClient.ConvertRegistersToLong(listInt); } else if (size == 2) { valore = ModbusClient.ConvertRegistersToInt(listInt); } break; } return valore; } 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 = 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; } private Dictionary getDataDictionary(Dictionary memItemList, ref Dictionary outVal) { Dictionary readErrorList = new Dictionary(); // inizializzo i valori bool[] listBool = new bool[1]; int[] listInt = new int[2]; // procedo x ogni valore configurato... foreach (var item in memItemList) { double valore = 0; bool dataOk = false; // in primis DEVO determinare di quale TIPO di valore ho bisogno... dalla PRIMA cifra di memAddr... modBusAddrType memAddrType = getAddrType(item.Value.memAddr); // in base al tipo leggo array... switch (memAddrType) { case modBusAddrType.Coil: listBool = readCoil(item.Value.index, item.Value.size); valore = listBool[0] ? 1 : 0; break; case modBusAddrType.DiscreteInput: listBool = readDiscrInputs(item.Value.index, item.Value.size); valore = listBool[0] ? 1 : 0; break; case modBusAddrType.InputRegister: listInt = readInputReg(item.Value.index, item.Value.size); valore = convertVal(listInt, item.Value.size, item.Value.tipoMem); break; case modBusAddrType.HoldingRegister: listInt = readHoldReg(item.Value.index - 1, item.Value.size); valore = convertVal(listInt, item.Value.size, item.Value.tipoMem); break; default: break; } // verifica limite... con delta da impianto dataOk = (valore > (item.Value.minVal + deltaVal) && valore < (item.Value.maxVal - deltaVal)); if (dataOk) { // moltiplico x fattore conversione... valore = valore * item.Value.factor; saveValue(ref outVal, valore, item.Key); } else { lgError($"getDynData: valore scartato x limiti min/max | {item.Key} | val: {valore} | min-max: {item.Value.minVal}-{item.Value.maxVal} | deltaVal: {deltaVal}"); readErrorList.Add(item.Key, item.Value); lgInfo($"--> rimesso in coda lettura | parametro: {item.Key} | index: {item.Value.index} | size: {item.Value.size}"); } } return readErrorList; } /// /// 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 ultimas crittura 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) { lgInfo(logValue); lastMemWrite[memAddrWrite] = adesso; } } #endregion Private Methods #region Protected Methods /// /// Decodifica il resto dell'area x i dati accessori (allarmi, ...) /// protected virtual void decodeOtherData() { } /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO/GWMS /// - per lo scopo specifico IN REALTA' non conta lo stato macchina.... ma lo inviamo lo stesso /// protected virtual void decodeToBaseBitmap() { // init a zero... B_input = 0; } /// /// Override metodo x scrittura parametri su PLC /// /// protected override void plcWriteParams(ref List updatedPar) { #if false 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($"---------------MemBlock data---------------{Environment.NewLine}{BitConverter.ToString(MemBlock)}{Environment.NewLine}--------------- END data ---------------"); if (!string.IsNullOrEmpty(memAddrWrite)) { // scrivo su ModBusTCP 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}"); } } } #endif } /// /// Imposto parametri PLC /// protected override void setParamPlc() { // Creo oggetto connessione NC parentForm.commPlcActive = true; lgInfo($"Start init Adapter ModBus TCP all'IP {cIobConf.cncIpAddr} | port: {cIobConf.cncPort} | --> IOB {cIobConf.codIOB}"); // SE è necessario refresh... if (needRefresh) { lgInfo("Refreshing connection..."); if (parametri != null) { try { parametri.ipAdrr = cIobConf.cncIpAddr; parametri.port = int.Parse(cIobConf.cncPort); // leggo file init... lgInfo("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... lgInfo("Set RawInput dimensions..."); RawInput = new byte[parametri.memSizeRead]; RawOutput = new byte[parametri.memSizeWrite]; // salvo parametri conn! lgInfo(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 { //Ip-Address and Port of Modbus-TCP-Server currPLC = new ModbusClient(parametri.ipAdrr, parametri.port); // disconnetto e connetto... if (isVerboseLog) { lgInfo("ModBus TCP: tryDisconnect"); } tryDisconnect(); // lo ripeto x evitare che ci sia un loop... e tryConnect richiami la procedura corrente... needRefresh = false; lgInfo("ModBus TCP: tryConnect"); tryConnect(); lgInfo("End init Adapter ModBusTCP"); if (isVerboseLog) { lgInfo("ModBus TCP CONNESSIONE AVVENUTA"); } } catch (Exception exc) { lgError(exc, "Errore in INIT ModBusTCP"); } } else { lgError($"Errore in ping: esito {esitoPing}"); } parentForm.commPlcActive = false; // carico conf vettore memoria... loadMemConf(); // aggiungo DELTA x calcolo min/MAX... string deltaValStr = getOptPar("DELTA_VAL"); if (!string.IsNullOrEmpty(deltaValStr)) { double.TryParse(deltaValStr, out deltaVal); } bool enableByApp = utils.CRB("enableContapezzi"); bool enableByIob = (getOptPar("ENABLE_PZCOUNT") == "TRUE"); bool disableByIob = (getOptPar("DISABLE_PZCOUNT") == "TRUE"); if ((enableByApp || enableByIob) && !(disableByIob)) { lgInfo("ModBus TCP: 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")) { lgInfo("Init contapezzi ModBusTCP: pzCntReload(true)"); pzCntReload(true); // refresh associazione Macchina - IOB sendM2IOB(); // per adesso imposto lettura PLC == contapezzi (poi farà vera lettura...) contapezziPLC = contapezziIOB; } else { contapezziIOB = 0; lgInfo("Contapezzi STD disabilitato: modalità {0}", getOptPar("PZCOUNT_MODE")); } } else { contapezziIOB = 0; lgInfo("Parametro mancante PZCOUNT_MODE"); } } catch (Exception exc) { lgError(exc, "Errore in contapezzi ModBusTCP"); } } } else { lgError("Parametri null!"); } } } /// /// effettua il setup dei memblock da gestire (NON leggo intera memoria ma tanti blocchi...) /// protected virtual void setupMemBlocks() { } /// /// Test connessione CNC /// /// protected bool testCncConn() { bool answ = currPLC.Connected; if (!answ) { // 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.Connected) { currPLC.Connect(); } if (!currPLC.Available(500)) { lgError($"PLC ModBus NON disponibile: {currPLC.IPAddress} | {currPLC.Port}"); } else { if (!currPLC.Connected) { lgError($"PLC ModBus NON connesso:{currPLC.IPAddress} | {currPLC.Port}"); } else { // tutto ok parentForm.updateComStats("Connection OK"); answ = true; } } } // salvo stato ping lastPingConn = adesso; } lastPingOk = answ; } return answ; } #endregion Protected Methods #region Public Methods /// /// Metodo dispose x il currPLC contenuto /// public void Dispose() { currPLC.Disconnect(); } /// /// Processo i task richiesti e li elimino dalla coda 1:1 /// /// public override Dictionary executeTasks(Dictionary task2exe) { lgInfo($"Chiamata executeTasks specifica ModBus TCP: {task2exe.Count} task ricevuti"); // Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti... Dictionary taskDone = new Dictionary(); return taskDone; } /// /// Decodifica tipo indirizzo dal codice /// vedere ad esempio https://www.fernhillsoftware.com/help/drivers/modbus/data-address-format.html /// /// /// public modBusAddrType getAddrType(string memAddr) { modBusAddrType answ = modBusAddrType.Coil; // leggo prima cifra... answ = (modBusAddrType)Enum.Parse(typeof(modBusAddrType), memAddr.Substring(0, 1)); return answ; } /// /// Recupero dati dinamici... /// ATTENZIONE factor usato come FONDOSCALA /// /// Esempio: /// - RealVal : 0 - 28000 /// - ReadVal : 0 - 100 /// - Factor : 28000 / 100 = 280 /// 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 ModBus TCP 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) { Dictionary readErrorList = new Dictionary(); Dictionary readErrorListRepeat = new Dictionary(); var memItemList = memMap.mMapRead; readErrorList = getDataDictionary(memItemList, ref outVal); // se qualcosa è andato storto riprovo a caricare SOLO gli errori... 1 sola volta if (readErrorList.Count > 0) { readErrorListRepeat = getDataDictionary(readErrorList, ref outVal); } // se avessi acnora errori --> disconnetto if (readErrorListRepeat.Count > 0) { lgInfo("Trovati valori non validi al secondo tentativo --> resetto adapter con tryDisconnect!"); //// rendo non valida lettura dati //lastReadPLC = DateTime.Now.AddMinutes(-4); tryDisconnect(); tryConnect(); } else { lastReadPLC = DateTime.Now; } } else { lgInfo($"getDynData: {memMap.mMapRead.Count} record in mMapRead"); } } catch (Exception exc) { lgError(exc, "Errore in getDynData x ModBus TCP PLC --> ciclo disconnect/reconnect"); tryDisconnect(); tryConnect(); } } } else { lgInfo($"Non processo getDynData: enableTSVC = false"); } if (periodicLog || outVal.Count > 0) { lgInfo($"Esito getDynData: {outVal.Count} valori VALIDI in outVal"); } return outVal; } /// /// Lettura valori Coils (1...) /// /// /// /// public bool[] readCoil(int startAddr, int qty) { bool[] answ = new bool[1]; if (currPLC.Connected) { answ = currPLC.ReadCoils(startAddr, qty); } return answ; } /// /// Lettura valori DiscreteInputs (2...) /// /// /// /// public bool[] readDiscrInputs(int startAddr, int qty) { bool[] answ = new bool[1]; if (currPLC.Connected) { answ = currPLC.ReadDiscreteInputs(startAddr, qty); } return answ; } /// /// Lettura valori Holding Register (3...) /// /// /// /// public int[] readHoldReg(int startAddr, int qty) { int[] answ = new int[2]; if (currPLC.Connected) { answ = currPLC.ReadHoldingRegisters(startAddr, qty); } return answ; } /// /// Lettura valori Input Register (4...) /// /// /// /// public int[] readInputReg(int startAddr, int qty) { int[] answ = new int[2]; if (currPLC.Connected) { answ = currPLC.ReadInputRegisters(startAddr, qty); } return answ; } /// /// Effettua lettura semafori principale /// Parametri da aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { // NON eseguo x NON indicare data ora ultima lettura se NON fatta davvero //base.readSemafori(ref currDispData); byte[] MemBlock = new byte[2]; try { currDispData.semIn = Semaforo.SV; if (verboseLog) { lgInfo("inizio read semafori"); } //// ciclo a leggere TUTTI i blocchi di memoria impostati //foreach (var item in memSetR) //{ // // leggo TUTTI i byte configurati... // MemBlock = new byte[item.Value]; // S7ReadBB(ref MemBlock, $"{item.Key}", item.Value); // Buffer.BlockCopy(MemBlock, 0, RawInput, item.Key, item.Value); //} //// leggo TUTTI i byte configurati... //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 false 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"); } 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; } #endif parentForm.commPlcActive = false; sw.Stop(); if (utils.CRB("recTime")) { TimingData.addResult(cIobConf.codIOB, string.Format("{0}|{1}", parametri.memAddrRead, numByte), sw.ElapsedTicks); } } 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 false 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: {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}"); } } #endif 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 false 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}"); } } #endif sw.Stop(); } } return answ; } /// /// Salvo in memblock il valore Int indicato con formattazione ModBus TCP /// /// 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 ModBus TCP /// /// 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 ModBus TCP /// /// 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 ModBus TCP /// /// 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}"); } } } /// /// Effettua salvataggio in LUT del valore ricevuto (double) /// /// /// /// /// public override void saveValue(ref Dictionary outVal, double valore, string chiave) { //check obj preliminare if (outVal == null) { outVal = new Dictionary(); } bool scaduto = stackVal_TSVC(chiave, valore); // recupero VC valore = getVal_TSVC(chiave, scaduto); if (scaduto) { outVal.Add(chiave, $"{valore}"); } LastTSVC[chiave] = valore; } /// /// Override connessione /// public override void tryConnect() { bool doLog = (verboseLog || periodicLog); lgInfo("ModBus TCP: tryConnect step 01"); if (!connectionOk) { // SE è necessario refresh... if (needRefresh) { lgInfo("ModBus TCP: tryConnect step 02"); // reimporto parametri PLC se necessario... setParamPlc(); } lgInfo("ModBus TCP: tryConnect step 03"); // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (doLog) { lgInfo("ModBus TCP: ConnKO - tryConnect"); } lgInfo("ModBus TCP: 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.Connect(); szStatusConnection = "OPEN"; parentForm.commPlcActive = false; connectionOk = currPLC.Connected; lgInfo($"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 ModBusTCP | szStatusConnection {szStatusConnection}{Environment.NewLine}{exc}"); connectionOk = false; needRefresh = true; } } else { // loggo no risposta ping ... connectionOk = false; if (doLog) { lgInfo($"Attenzione: ModBusTCP 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() { lgInfo("Richiesta disconnessione adapter ModBusTCP!"); try { currPLC.Disconnect(); connectionOk = false; lgInfo("Effettuata disconnessione adapter ModBusTCP!"); } catch (Exception exc) { lgFatal($"Errore nella disconnessione dall'adapter ModBusTCP{Environment.NewLine}{exc}"); } } #endregion Public Methods } }