using EasyModbus; using IOB_UT_NEXT; using MapoSDK; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; namespace IOB_WIN_NEXT { /* -------------------------------------------------------------------------------- * Controlli ModBusTCP COMECA * - protocollo ModBus TCP * * -------------------------------------------------------------------------------- */ public class IobModbusTCP : IobGeneric { #region Protected Fields protected ModbusClient currPLC; /// /// 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; #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 Methods /// /// 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(); 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() { } #endregion Protected Methods #region Public Methods /// /// 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; } /// /// 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() { if (connectionOk) { string szStatusConnection = ""; try { currPLC.Disconnect(); connectionOk = false; lgInfo(szStatusConnection); lgInfo("Effettuata disconnessione adapter ModBusTCP!"); } catch (Exception exc) { lgFatal(exc, "Errore nella disconnessione dall'adapter ModBusTCP"); } } else { lgError("IMPOSSIBILE effettuare disconnessione ModBusTCP: Connessione non disponibile..."); } } #endregion Public Methods } }