diff --git a/IOB-WIN-NEXT/IobModbusTCP.cs b/IOB-WIN-NEXT/IobModbusTCP.cs index 6be7f7c8..2b422082 100644 --- a/IOB-WIN-NEXT/IobModbusTCP.cs +++ b/IOB-WIN-NEXT/IobModbusTCP.cs @@ -1,9 +1,11 @@ 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; @@ -13,22 +15,72 @@ namespace IOB_WIN_NEXT { #region Protected Fields - protected string serverIp = "hampizzaferri.dyndns.org"; + 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 - //protected string serverIp = "127.0.0.1"; - #region Public Constructors - /// Classe base con i metodi x Siemens + /// 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!"); + } + + // provo una lettura testRead(); } @@ -36,65 +88,26 @@ namespace IOB_WIN_NEXT #region Private Methods - private void testConf() + /// + /// effettua il setup dei memblock da gestire (NON leggo intera memoria ma tanti blocchi...) + /// + private void setupMemBlocks() { - BaseModbusConf.VarConf var01 = new BaseModbusConf.VarConf() - { - Name = "Giacenza Serbatoio", - Type = BaseModbusConf.DataType.Real, - MemAddr = 1, - Size = 2, - Direction = BaseModbusConf.MemDirection.R, - RangeMin = 0, - RangeMax = 100 - }; - BaseModbusConf.VarConf var02 = new BaseModbusConf.VarConf() - { - Name = "Pressione Serbatoio", - Type = BaseModbusConf.DataType.Real, - MemAddr = 3, - Size = 2, - Direction = BaseModbusConf.MemDirection.R, - RangeMin = 0, - RangeMax = 25 - }; - List memList01 = new List(); - memList01.Add(var01); - memList01.Add(var02); - BaseModbusConf.ModbusMemArea block01 = new BaseModbusConf.ModbusMemArea() - { - RawAddr = "40001", - Type = BaseModbusConf.MemType.HoldingRegister, - BaseAddr = 1, - Size = 50, - VarList = memList01 - }; - List DemoMem = new List(); - DemoMem.Add(block01); - - // creo un area di memoria - BaseModbusConf demoConf = new BaseModbusConf() - { - MemoryList = DemoMem - }; - // salvo conf con json x compilare... - var rawData = JsonConvert.SerializeObject(demoConf); + // da calcolare... ora setup cablato... + memSetR.Add(1, 34); } private void testRead() { //Ip-Address and Port of Modbus-TCP-Server - ModbusClient modbusClient = new ModbusClient(serverIp, 502); + currPLC = new ModbusClient(cIobConf.cncIpAddr, 502); //Connect to Server - modbusClient.Connect(); + currPLC.Connect(); //modbusClient.WriteMultipleCoils(4, new bool[] { true, true, true, true, true, true, true, true, true, true }); //Write Coils starting with Address 5 //bool[] readCoils = modbusClient.ReadCoils(9, 10); //Read 10 Coils from Server, starting with address 10 - int[] readHoldingRegisters = modbusClient.ReadHoldingRegisters(0, 34); //Read 10 Holding Registers from Server, starting with Address 1 + int[] readHoldingRegisters = currPLC.ReadHoldingRegisters(0, 34); //Read 10 Holding Registers from Server, starting with Address 1 - int[] readHR1000 = modbusClient.ReadHoldingRegisters(0, 100); //Read 10 Holding Registers from Server, starting with Address 1 - - //Disconnect from Server - modbusClient.Disconnect(); + int[] readHR1000 = currPLC.ReadHoldingRegisters(0, 100); //Read 10 Holding Registers from Server, starting with Address 1 //// Console Output //for (int i = 0; i < readCoils.Length; i++) @@ -109,8 +122,375 @@ namespace IOB_WIN_NEXT } Console.Write("Press any key to continue . . . "); Console.ReadKey(true); + +#if false + //Disconnect from Server + currPLC.Disconnect(); +#endif } #endregion Private Methods + + #region Protected Methods + + /// + /// 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("ModBusTCP: tryDisconnect"); + } + tryDisconnect(); + + // lo ripeto x evitare che ci sia un loop... e tryConnect richiami la procedura corrente... + needRefresh = false; + lgInfo("ModBusTCP: tryConnect"); + tryConnect(); + lgInfo("End init Adapter ModBusTCP"); + if (isVerboseLog) + { + lgInfo("ModBusTCP 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("ModBusTCP: 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!"); + } + } + } + + #endregion Protected Methods + + #region Public Methods + + /// + /// Override connessione + /// + public override void tryConnect() + { + bool doLog = (verboseLog || periodicLog); + lgInfo("ModBusTCP: tryConnect step 01"); + if (!connectionOk) + { + // SE è necessario refresh... + if (needRefresh) + { + lgInfo("ModBusTCP: tryConnect step 02"); + + // reimporto parametri PLC se necessario... + setParamPlc(); + } + lgInfo("ModBusTCP: 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("ModBusTCP: ConnKO - tryConnect"); + } + lgInfo("ModBusTCP: 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 } } \ No newline at end of file diff --git a/IOB-WIN-NEXT/IobSiemens.cs b/IOB-WIN-NEXT/IobSiemens.cs index 62a86bfa..b82ab5ec 100644 --- a/IOB-WIN-NEXT/IobSiemens.cs +++ b/IOB-WIN-NEXT/IobSiemens.cs @@ -416,7 +416,7 @@ namespace IOB_WIN_NEXT } /// - /// OVerride metodo x scrittura parametri su PLC + /// Override metodo x scrittura parametri su PLC /// /// protected override void plcWriteParams(ref List updatedPar) diff --git a/IOB-WIN-NEXT/specialConfig.cs b/IOB-WIN-NEXT/specialConfig.cs index 6a867dcf..c85e2f94 100644 --- a/IOB-WIN-NEXT/specialConfig.cs +++ b/IOB-WIN-NEXT/specialConfig.cs @@ -2,6 +2,52 @@ namespace IOB_WIN_NEXT { + /// + /// Implementazione classe connessione ModBus TCP, + /// comprensiva dei parametri delle aree di memoria + /// + public class connParamModBusTCP + { + #region Public Fields + + /// + /// Indirizzo IP del PLC + /// + public string ipAdrr = ""; + + /// + /// Base area x lettura + /// + public string memAddrRead = ""; + + /// + /// Base area x scrittura + /// + public string memAddrWrite = ""; + + /// + /// Size memoria lettura + /// + public int memSizeRead = 0; + + /// + /// Size memoria scrittura + /// + public int memSizeWrite = 0; + + /// + /// Timeout ping + /// + public int pingMsTimeout = 250; + + /// + /// Porta di comunicazione + /// + public int port; + + #endregion Public Fields + } + /// /// Implementazione classe connessione SIEMENS con S7.net, /// comprensiva dei parametri delle aree di memoria