diff --git a/IOB-WIN-NEXT/DATA/CONF/FINASSI_HELPI_01.ini b/IOB-WIN-NEXT/DATA/CONF/FINASSI_HELPI_01.ini index 0af3ab79..1dce7177 100644 --- a/IOB-WIN-NEXT/DATA/CONF/FINASSI_HELPI_01.ini +++ b/IOB-WIN-NEXT/DATA/CONF/FINASSI_HELPI_01.ini @@ -1,12 +1,12 @@ ;Configurazione IOB-WIN [IOB] -;Impianto Cedax di Turi (test) - Giacovelli -CNCTYPE=MODBUS_TCP_CEDAX +;Impianto Helpi x impacchettatrice - Finassi +CNCTYPE=MODBUS_TCP_HELPI PING_MS_TIMEOUT=1000 [MACHINE] -VENDOR=CEDAX -MODEL=CEDAX +VENDOR=HELPI +MODEL=HELPI [CNC] IP=10.74.82.65 @@ -25,9 +25,9 @@ CMDREBO=/IOB/sendReboot?idxMacchina= ;CMDREBO=/sendReboot.aspx?idxMacchina= [MEMORY] -ADDR_READ=41001 -ADDR_WRITE=41021 -SIZE_READ=20 +ADDR_READ=41060 +ADDR_WRITE=41060 +SIZE_READ=0 SIZE_WRITE=0 HR_BASE_ADDR=40000 @@ -48,7 +48,7 @@ BLINK_FILT=0 [OPTPAR] ;PZCOUNT_MODE=STD.[PAR/MEM].info|BIT.indice -PZCOUNT_MODE=STD.DB85.DBRE16 +PZCOUNT_MODE=STD.41100.UDINT32 DISABLE_PZCOUNT=TRUE ENABLE_SEND_PZC_BLOCK=TRUE MIN_SEND_PZC_BLOCK=0 @@ -63,13 +63,13 @@ DELTA_VAL=0.1 timerIntMs=10 ; conf parametri memoria READ/WRITE -PARAM_CONF=GIACO_CEDAX_01.json +PARAM_CONF=FINASSI_HELPI_01.json -NO_PING=TRUE +NO_PING=FALSE ; conf blocchi memoria x READ -MEM_BLOCK=GIACO_CEDAX_01_MBlock.json +MEM_BLOCK=FINASSI_HELPI_01_MBlock.json ; conf aree allarme -ALARM_CONF=GIACO_CEDAX_01_alarm.json +ALARM_CONF=FINASSI_HELPI_01_alarm.json [BRANCH] NAME=master \ No newline at end of file diff --git a/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj b/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj index 22f5e805..1a148125 100644 --- a/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj +++ b/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj @@ -260,6 +260,7 @@ + Always @@ -278,6 +279,18 @@ Always + + Always + + + Always + + + Always + + + Always + Always diff --git a/IOB-WIN-NEXT/IobModbusTCP.cs b/IOB-WIN-NEXT/IobModbusTCP.cs index f079af54..4c1d8279 100644 --- a/IOB-WIN-NEXT/IobModbusTCP.cs +++ b/IOB-WIN-NEXT/IobModbusTCP.cs @@ -6,11 +6,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Net.NetworkInformation; -using System.Text; using System.Threading; -using System.Threading.Tasks; using System.Windows.Forms; namespace IOB_WIN_NEXT @@ -23,61 +20,9 @@ namespace IOB_WIN_NEXT public class IobModbusTCP : IobGeneric { - #region Private Fields - - /// - /// Valore delta x gestione min/MAX e valore rilevato - /// - private double deltaVal = 0; - /// - /// Indirizzo di base x fare il calcolo di dove leggere in ModBus - /// - private int HoldRegBaseAddr = 40001; - - #endregion Private Fields - - #region Protected Fields - - protected ModbusClient currPLC; - - /// - /// Copia locale dei valori in Holding Registry, come array chiave (int) valori int[] letti tramite ModBus, da convertire secondo tipo - /// - protected Dictionary HoldingRegisterLUT = new Dictionary(); - - /// - /// 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(); - - /// - /// 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 - /// - /// - /// + /// Classe base con i metodi x ModBusTCP public IobModbusTCP(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { lgInfo("NEW IOB ModBus TCP"); @@ -122,766 +67,6 @@ namespace IOB_WIN_NEXT #endregion Public Constructors - #region Protected Properties - - protected bool hasAlarms - { - get - { - bool answ = false; - int numErrors = 0; - int currStatus = 0; - int[] listInt = new int[2]; - if (alarmMaps != null) - { - // leggo a ciclo le aree degli allarmi CONFIGURATI, se ne trovo --> segnalo allarme... - foreach (var item in alarmMaps) - { - // banchi in array Int16 --> scompongo - for (int i = 0; i < item.size / 2; i++) - { - // verifico se usare LUT - bool useLUT = memSetR != null && memSetR.Count > 0; - if (useLUT) - { - int lutAddress = 40000 + item.index; - if (HoldingRegisterLUT.ContainsKey(lutAddress)) - { - try - { - listInt = HoldingRegisterLUT[lutAddress]; - } - catch (Exception exc) - { - lgError($"Errore in lettura da HoldingRegisterLUT per indirizzo {lutAddress} | item {item.memAddr}{Environment.NewLine}{exc}"); - } - } - } - else - { - listInt = readInputReg(item.index, item.size); - } - currStatus = ModbusClient.ConvertRegistersToInt(listInt); - - // aggiornamento blink counters dato nuovo valore - item.checkBlinkCounter(i, (uint)currStatus); - - // calcolo indice errori - if ((uint)(item.alarmsMask[i] & currStatus) > 0) - { - numErrors++; - } - // verifico SE sia variato... confronto allarmi filtrato stile blink per bit status: - // - allarmi che iniziano per # IGNORATI - // - altri allarmi con un countdown da MAX_COUNTER_BLINK a 0 per il fronte di discesa - if (item.isChanged(i, (uint)currStatus)) - { - // registro gli allarmi attivi e trasmetto... - if (sendAlarmVariations(item.memAddr, i, item.alarmsState[i], (uint)(item.alarmsMask[i] & currStatus), item.messages)) - { - // se inviato --> salvo stato da current... - item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus)); - } - } - } - } - } - answ = numErrors > 0; - return answ; - } - } - - /// - /// 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 convertFromReg(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; - - // caso speciale x Cedax, che usa HighVal moltiplicato x 32768... - case plcDataType.HLPInt: - if (size == 4) - { - valore = ModbusClient.ConvertRegistersToLong(listInt); - } - else if (size == 2) - { - int fact = Int16.MaxValue + 1; - valore = listInt[1] * fact + listInt[0]; - } - 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 double getScaledDouble(dataConf currMem) - { - double valDouble; - // prima faccio eventuale fattore di scala... - double.TryParse(currMem.value, out valDouble); - if (currMem.factor != 1) - { - valDouble = valDouble * (double)currMem.factor; - } - - return valDouble; - } - - 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; - } - - /// - /// se si disconnette --> processo tentativo riconnessione.. - /// - /// - private void CurrPLC_ConnectedChanged(object sender) - { - if (!currPLC.Connected) - { - tryDisconnect(); - } - } - - /// - /// Recupero dati da singole letture - /// - /// Valori da processare - /// Impiego della LookUpTable x diminuire accessi - /// Dizionario valori in uscita - /// Errori di lettura - private Dictionary getDataDictionary(Dictionary memItemList, bool useLUT, 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) - { - // attesa 50 ms prima di procedere x evitare burst dati - if (!useLUT) - { - Thread.Sleep(50); - } - double valore = 0; - double valoreScal = 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 = convertFromReg(listInt, item.Value.size, item.Value.tipoMem); - - break; - - case modBusAddrType.HoldingRegister: - if (useLUT) - { - int lutAddress = 40000 + item.Value.index; - if (HoldingRegisterLUT.ContainsKey(lutAddress)) - { - try - { - listInt = HoldingRegisterLUT[lutAddress]; - } - catch (Exception exc) - { - lgError($"Errore in lettura da HoldingRegisterLUT per indirizzo {lutAddress} | item {item.Key}{Environment.NewLine}{exc}"); - } - } - } - else - { - listInt = readHoldReg(item.Value.index - 1, item.Value.size); - } - valore = convertFromReg(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... - valoreScal = valore * item.Value.factor; - saveValue(ref outVal, valoreScal, item.Key); - lgDebug($"getDynData: valore ricevuto | {item.Key} | val: {valore} | valoreScal: {valoreScal}"); - } - else - { - lgError($"getDynData: valore scartato x limiti min/max | {item.Key} | val: {valore} | valoreScal: {valoreScal} | min-max: {item.Value.minVal}-{item.Value.maxVal} | deltaVal: {deltaVal}"); - readErrorList.Add(item.Key, item.Value); - lgDebug($"--> 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; - } - } - - /// - /// Effettua lettura blocco memoria indicato e lo salva in copia locale HoldingRegisterLUT - /// - /// Indirizzo di aprtenza - /// NUm registri da leggere (max 120) - private void readBlockHoldingReg(int startAddr, int numReg, int baseAddr) - { - bool allOk = false; - if (currPLC.Connected) - { - allOk = true; - // fix massima lunghezza pacchetto - if (numReg > 120) - { - lgError($"Attenzione richiesta lettura blocco troppo grande: numReg {numReg} --> 120"); - numReg = 120; - } - // si lavora rispetto all'indirizzo base 40001 - - int[] rawData = new int[2]; - try - { - stopwatch.Restart(); - rawData = currPLC.ReadHoldingRegisters(startAddr - baseAddr, numReg); - stopwatch.Stop(); - lgTrace($"Lettura in blocco HoldingRegisters| startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {stopwatch.ElapsedMilliseconds}ms"); - // salvo in LUT la versione ESPLOSA 2 byte alla volta... - if (rawData.Length > 0) - { - for (int i = 0; i < rawData.Length / 2; i++) - { - int[] thisSet = new int[2]; - if (rawData.Length >= (i + 1) * 2) - { - Array.Copy(rawData, i * 2, thisSet, 0, 2); - // salvo nel registro... - int currAddr = startAddr + i * 2; - if (HoldingRegisterLUT.ContainsKey(currAddr)) - { - HoldingRegisterLUT[currAddr] = thisSet; - } - else - { - HoldingRegisterLUT.Add(currAddr, thisSet); - } - } - else - { - currReadErrors++; - allOk = false; - lgError($"Impossibile copiare dati, array di origine troppo corto: rawData.lenght: {rawData.Length} | base addr richiesto i*2: {i * 2} | errori currReadErrors: {currReadErrors}"); - } - } - } - } - catch (Exception exc) - { - currReadErrors++; - allOk = false; - lgError($"Eccezione in readBlockHoldingReg | currReadErrors: {currReadErrors}{Environment.NewLine}{exc}"); - } - } - else - { - lgError($"Attenzione, plc disconnesso... currReadErrors: {currReadErrors}"); - tryDisconnect(); - } - - // se tutto ok --> riduco contatore errori di 1... - if (allOk) - { - currReadErrors = currReadErrors >= 1 ? currReadErrors-- : 0; - } - else - { - // se > max errori --> disconnetto - if (currReadErrors > maxReadErrors) - { - lgError($"Superato limite errori Read ({currReadErrors}) --> tryDisconnect"); - currReadErrors = 0; - tryDisconnect(); - } - else - { - // altrimenti pausa forzata - Thread.Sleep(300); - } - } - } - - #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) - { - dataConf currMem = null; - int byteSize = 0; - int[] CurrVal = new int[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; - double valDouble = 0; - // cerco in area memMapWrite... - if (memMap.mMapWrite.ContainsKey(item.uid)) - { - // recupero! - currMem = memMap.mMapWrite[item.uid]; - byteSize = currMem.size; - memAddrWrite = currMem.memAddr; - CurrVal = new int[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); - CurrVal = ModbusClient.ConvertIntToRegisters(valInt); - fatto = writeInputReg(currMem.index, CurrVal); - break; - - case plcDataType.DInt: - valUInt = getScaledUInt(currMem); - CurrVal = ModbusClient.ConvertLongToRegisters(valInt); - fatto = writeInputReg(currMem.index, CurrVal); - 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: - valDouble = getScaledDouble(currMem); - CurrVal = ModbusClient.ConvertFloatToRegisters((float)valDouble, ModbusClient.RegisterOrder.HighLow); - fatto = writeInputReg(currMem.index, CurrVal); - 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}CurrVal: {CurrVal}{Environment.NewLine}--------------- END data ---------------"); - if (!string.IsNullOrEmpty(memAddrWrite)) - { - // se fatto --> aggiorno! - if (fatto) - { - item.value = item.reqValue; - item.reqValue = ""; - item.lastRead = DateTime.Now; - item.UM = currMem.unit; - } - } - else - { - lgError($"Errore: memAddrWrite vuoto!"); - } - } - else - { - lgError($"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() - { - DateTime adesso = DateTime.Now; - // 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) - { - lgInfoStartup("Refreshing connection..."); - if (parametri != null) - { - try - { - parametri.ipAdrr = cIobConf.cncIpAddr; - parametri.port = int.Parse(cIobConf.cncPort); - // 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); - parametri.holdRegBaseAddr = fIni.ReadInteger("MEMORY", "HR_BASE_ADDR", 40001); - // 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"); - } - - // carico conf vettore memoria... - loadMemConf(); - // avvio conf blocchi memoria - setupMemBlocks(); - // aggiungo DELTA x calcolo min/MAX... - string deltaValStr = getOptPar("DELTA_VAL"); - if (!string.IsNullOrEmpty(deltaValStr)) - { - double.TryParse(deltaValStr.Replace(".", ","), out deltaVal); - } - bool enableByApp = utils.CRB("enableContapezzi"); - bool enableByIob = (getOptPar("ENABLE_PZCOUNT") == "TRUE"); - bool disableByIob = (getOptPar("DISABLE_PZCOUNT") == "TRUE"); - if ((enableByApp || enableByIob) && !(disableByIob)) - { - lgInfoStartup("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")) - { - lgInfoStartup("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; - lgDebug("Contapezzi STD disabilitato: modalità {0}", getOptPar("PZCOUNT_MODE")); - } - } - else - { - contapezziIOB = 0; - lgError("Parametro mancante PZCOUNT_MODE"); - } - } - catch (Exception exc) - { - lgError(exc, "Errore in contapezzi ModBusTCP"); - } - } - - // ora tento avvio PLC... SE PING OK... - lastPING = adesso; - 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); - currPLC.ConnectionTimeout = 3000; - - currPLC.ConnectedChanged += CurrPLC_ConnectedChanged; - - // disconnetto e connetto... - if (isVerboseLog) - { - lgInfoStartup("ModBus TCP: tryDisconnect"); - } - tryDisconnect(); - - // lo ripeto x evitare che ci sia un loop... e tryConnect richiami la procedura corrente... - needRefresh = false; - lgInfoStartup("ModBus TCP: tryConnect"); - tryConnect(); - lgInfoStartup("End init Adapter ModBusTCP"); - if (isVerboseLog) - { - lgInfo("ModBus TCP CONNESSIONE AVVENUTA"); - } - } - catch (Exception exc) - { - lgError(exc, "Errore in INIT ModBusTCP"); - } - } - else - { - lgError($"ModBusTCP IOB | Errore in ping: esito {esitoPing}"); - } - parentForm.commPlcActive = false; - } - else - { - lgError("Parametri null!"); - } - } - } - - /// - /// effettua il setup dei memblock da gestire (NON leggo intera memoria ma tanti blocchi...) - /// - protected void setupMemBlocks() - { - // se configurato -_> deserializzo - string confFile = getOptPar("MEM_BLOCK"); - if (!string.IsNullOrEmpty(confFile)) - { - string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{confFile}"; - lgInfo($"Apertura file {jsonFileName}"); - using (StreamReader reader = new StreamReader(jsonFileName)) - { - string jsonData = reader.ReadToEnd(); - if (!string.IsNullOrEmpty(jsonData)) - { - lgInfo($"File json MemBlock composto da {jsonData.Length} caratteri"); - try - { - var currMem = JsonConvert.DeserializeObject(jsonData); - memSetR = currMem.ReadBlocks; - } - catch (Exception exc) - { - lgError($"Errore in setupMemBlock{Environment.NewLine}{exc}"); - } - } - } - } - } - - /// - /// 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 /// @@ -962,8 +147,7 @@ namespace IOB_WIN_NEXT } /// - /// Decodifica tipo indirizzo dal codice - /// vedere ad esempio https://www.fernhillsoftware.com/help/drivers/modbus/data-address-format.html + /// Decodifica tipo indirizzo dal codice vedere ad esempio https://www.fernhillsoftware.com/help/drivers/modbus/data-address-format.html /// /// /// @@ -976,13 +160,12 @@ namespace IOB_WIN_NEXT } /// - /// Recupero dati dinamici... - /// ATTENZIONE factor usato come FONDOSCALA + /// Recupero dati dinamici... ATTENZIONE factor usato come FONDOSCALA /// /// Esempio: - /// - RealVal : 0 - 28000 - /// - ReadVal : 0 - 100 - /// - Factor : 28000 / 100 = 280 + /// - RealVal : 0 - 28000 + /// - ReadVal : 0 - 100 + /// - Factor : 28000 / 100 = 280 /// public override Dictionary getDynData() { @@ -1018,7 +201,8 @@ namespace IOB_WIN_NEXT var memItemList = memMap.mMapRead; readErrorList = getDataDictionary(memItemList, useLUT, ref outVal); - // se qualcosa è andato storto riprovo a caricare SOLO gli errori... 1 sola volta + // se qualcosa è andato storto riprovo a caricare SOLO gli errori... 1 + // sola volta if (readErrorList.Count > 0) { lgDebug($"Attesa prima di rilettura | LUT: {useLUT}"); @@ -1138,8 +322,8 @@ namespace IOB_WIN_NEXT } /// - /// Effettua lettura semafori principale - /// Parametri da aggiornare x display in form + /// Effettua lettura semafori principale Parametri da + /// aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { @@ -1351,5 +535,822 @@ namespace IOB_WIN_NEXT } #endregion Public Methods + + #region Protected Fields + + protected ModbusClient currPLC; + + /// + /// Copia locale dei valori in Holding Registry, come array chiave (int) valori int[] letti + /// tramite ModBus, da convertire secondo tipo + /// + protected Dictionary HoldingRegisterLUT = new Dictionary(); + + /// + /// 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(); + + /// + /// parametri di connessione + /// + protected connParamModBusTCP parametri; + + /// + /// Oggetto cronometro x test vari... + /// + protected Stopwatch sw = new Stopwatch(); + + #endregion Protected Fields + + #region Protected Properties + + protected bool hasAlarms + { + get + { + bool answ = false; + int numErrors = 0; + int currStatus = 0; + int[] listInt = new int[2]; + if (alarmMaps != null) + { + // leggo a ciclo le aree degli allarmi CONFIGURATI, se ne trovo --> segnalo allarme... + foreach (var item in alarmMaps) + { + // banchi in array Int16 --> scompongo + for (int i = 0; i < item.size / 2; i++) + { + // verifico se usare LUT + bool useLUT = memSetR != null && memSetR.Count > 0; + if (useLUT) + { + int lutAddress = 40000 + item.index; + if (HoldingRegisterLUT.ContainsKey(lutAddress)) + { + try + { + listInt = HoldingRegisterLUT[lutAddress]; + } + catch (Exception exc) + { + lgError($"Errore in lettura da HoldingRegisterLUT per indirizzo {lutAddress} | item {item.memAddr}{Environment.NewLine}{exc}"); + } + } + } + else + { + listInt = readInputReg(item.index, item.size); + } + currStatus = ModbusClient.ConvertRegistersToInt(listInt); + + // aggiornamento blink counters dato nuovo valore + item.checkBlinkCounter(i, (uint)currStatus); + + // calcolo indice errori + if ((uint)(item.alarmsMask[i] & currStatus) > 0) + { + numErrors++; + } + // verifico SE sia variato... confronto allarmi filtrato stile blink per + // bit status: + // - allarmi che iniziano per # IGNORATI + // - altri allarmi con un countdown da MAX_COUNTER_BLINK a 0 per il + // fronte di discesa + if (item.isChanged(i, (uint)currStatus)) + { + // registro gli allarmi attivi e trasmetto... + if (sendAlarmVariations(item.memAddr, i, item.alarmsState[i], (uint)(item.alarmsMask[i] & currStatus), item.messages)) + { + // se inviato --> salvo stato da current... + item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus)); + } + } + } + } + } + answ = numErrors > 0; + return answ; + } + } + + /// + /// 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 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 macchine base (GWMS) IN REALTA' non gestito lo stato macchina.... previsto comunque + /// + protected virtual void decodeToBaseBitmap() + { + // init a zero... + B_input = 0; + } + + /// + /// Override metodo x scrittura parametri su PLC + /// + /// + protected override void plcWriteParams(ref List updatedPar) + { + dataConf currMem = null; + int byteSize = 0; + int[] CurrVal = new int[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; + double valDouble = 0; + // cerco in area memMapWrite... + if (memMap.mMapWrite.ContainsKey(item.uid)) + { + // recupero! + currMem = memMap.mMapWrite[item.uid]; + byteSize = currMem.size; + memAddrWrite = currMem.memAddr; + CurrVal = new int[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); + CurrVal = ModbusClient.ConvertIntToRegisters(valInt); + fatto = writeInputReg(currMem.index, CurrVal); + break; + + case plcDataType.DInt: + valUInt = getScaledUInt(currMem); + CurrVal = ModbusClient.ConvertLongToRegisters(valInt); + fatto = writeInputReg(currMem.index, CurrVal); + 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: + valDouble = getScaledDouble(currMem); + CurrVal = ModbusClient.ConvertFloatToRegisters((float)valDouble, ModbusClient.RegisterOrder.HighLow); + fatto = writeInputReg(currMem.index, CurrVal); + 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}CurrVal: {CurrVal}{Environment.NewLine}--------------- END data ---------------"); + if (!string.IsNullOrEmpty(memAddrWrite)) + { + // se fatto --> aggiorno! + if (fatto) + { + item.value = item.reqValue; + item.reqValue = ""; + item.lastRead = DateTime.Now; + item.UM = currMem.unit; + } + } + else + { + lgError($"Errore: memAddrWrite vuoto!"); + } + } + else + { + lgError($"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() + { + DateTime adesso = DateTime.Now; + // 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) + { + lgInfoStartup("Refreshing connection..."); + if (parametri != null) + { + try + { + parametri.ipAdrr = cIobConf.cncIpAddr; + parametri.port = int.Parse(cIobConf.cncPort); + // 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); + parametri.holdRegBaseAddr = fIni.ReadInteger("MEMORY", "HR_BASE_ADDR", 40001); + // 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"); + } + + // carico conf vettore memoria... + loadMemConf(); + // avvio conf blocchi memoria + setupMemBlocks(); + // aggiungo DELTA x calcolo min/MAX... + string deltaValStr = getOptPar("DELTA_VAL"); + if (!string.IsNullOrEmpty(deltaValStr)) + { + double.TryParse(deltaValStr.Replace(".", ","), out deltaVal); + } + bool enableByApp = utils.CRB("enableContapezzi"); + bool enableByIob = (getOptPar("ENABLE_PZCOUNT") == "TRUE"); + bool disableByIob = (getOptPar("DISABLE_PZCOUNT") == "TRUE"); + if ((enableByApp || enableByIob) && !(disableByIob)) + { + lgInfoStartup("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")) + { + lgInfoStartup("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; + lgDebug("Contapezzi STD disabilitato: modalità {0}", getOptPar("PZCOUNT_MODE")); + } + } + else + { + contapezziIOB = 0; + lgError("Parametro mancante PZCOUNT_MODE"); + } + } + catch (Exception exc) + { + lgError(exc, "Errore in contapezzi ModBusTCP"); + } + } + + // ora tento avvio PLC... SE PING OK... + lastPING = adesso; + 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); + currPLC.ConnectionTimeout = 3000; + + currPLC.ConnectedChanged += CurrPLC_ConnectedChanged; + + // disconnetto e connetto... + if (isVerboseLog) + { + lgInfoStartup("ModBus TCP: tryDisconnect"); + } + tryDisconnect(); + + // lo ripeto x evitare che ci sia un loop... e tryConnect richiami la + // procedura corrente... + needRefresh = false; + lgInfoStartup("ModBus TCP: tryConnect"); + tryConnect(); + lgInfoStartup("End init Adapter ModBusTCP"); + if (isVerboseLog) + { + lgInfo("ModBus TCP CONNESSIONE AVVENUTA"); + } + } + catch (Exception exc) + { + lgError(exc, "Errore in INIT ModBusTCP"); + } + } + else + { + lgError($"ModBusTCP IOB | Errore in ping: esito {esitoPing}"); + } + parentForm.commPlcActive = false; + } + else + { + lgError("Parametri null!"); + } + } + } + + /// + /// effettua il setup dei memblock da gestire (NON leggo intera memoria ma tanti blocchi...) + /// + protected void setupMemBlocks() + { + // se configurato -_> deserializzo + string confFile = getOptPar("MEM_BLOCK"); + if (!string.IsNullOrEmpty(confFile)) + { + string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{confFile}"; + lgInfo($"Apertura file {jsonFileName}"); + using (StreamReader reader = new StreamReader(jsonFileName)) + { + string jsonData = reader.ReadToEnd(); + if (!string.IsNullOrEmpty(jsonData)) + { + lgInfo($"File json MemBlock composto da {jsonData.Length} caratteri"); + try + { + var currMem = JsonConvert.DeserializeObject(jsonData); + memSetR = currMem.ReadBlocks; + } + catch (Exception exc) + { + lgError($"Errore in setupMemBlock{Environment.NewLine}{exc}"); + } + } + } + } + } + + /// + /// 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 Private Fields + + /// + /// Valore delta x gestione min/MAX e valore rilevato + /// + private double deltaVal = 0; + + /// + /// Indirizzo di base x fare il calcolo di dove leggere in ModBus + /// + private int HoldRegBaseAddr = 40001; + + #endregion Private Fields + + #region Private Methods + + /// + /// converte valore in tipo desiderato + /// + /// + /// + /// + /// + private static double convertFromReg(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; + + // caso speciale x Cedax, che usa HighVal moltiplicato x 32768... + case plcDataType.HLPInt: + if (size == 4) + { + valore = ModbusClient.ConvertRegistersToLong(listInt); + } + else if (size == 2) + { + int fact = Int16.MaxValue + 1; + valore = listInt[1] * fact + listInt[0]; + } + 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 double getScaledDouble(dataConf currMem) + { + double valDouble; + // prima faccio eventuale fattore di scala... + double.TryParse(currMem.value, out valDouble); + if (currMem.factor != 1) + { + valDouble = valDouble * (double)currMem.factor; + } + + return valDouble; + } + + 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; + } + + /// + /// se si disconnette --> processo tentativo riconnessione.. + /// + /// + private void CurrPLC_ConnectedChanged(object sender) + { + if (!currPLC.Connected) + { + tryDisconnect(); + } + } + + /// + /// Recupero dati da singole letture + /// + /// Valori da processare + /// Impiego della LookUpTable x diminuire accessi + /// Dizionario valori in uscita + /// Errori di lettura + private Dictionary getDataDictionary(Dictionary memItemList, bool useLUT, 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) + { + // attesa 50 ms prima di procedere x evitare burst dati + if (!useLUT) + { + Thread.Sleep(50); + } + double valore = 0; + double valoreScal = 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 = convertFromReg(listInt, item.Value.size, item.Value.tipoMem); + + break; + + case modBusAddrType.HoldingRegister: + if (useLUT) + { + int lutAddress = 40000 + item.Value.index; + if (HoldingRegisterLUT.ContainsKey(lutAddress)) + { + try + { + listInt = HoldingRegisterLUT[lutAddress]; + } + catch (Exception exc) + { + lgError($"Errore in lettura da HoldingRegisterLUT per indirizzo {lutAddress} | item {item.Key}{Environment.NewLine}{exc}"); + } + } + } + else + { + listInt = readHoldReg(item.Value.index - 1, item.Value.size); + } + valore = convertFromReg(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... + valoreScal = valore * item.Value.factor; + saveValue(ref outVal, valoreScal, item.Key); + lgDebug($"getDynData: valore ricevuto | {item.Key} | val: {valore} | valoreScal: {valoreScal}"); + } + else + { + lgError($"getDynData: valore scartato x limiti min/max | {item.Key} | val: {valore} | valoreScal: {valoreScal} | min-max: {item.Value.minVal}-{item.Value.maxVal} | deltaVal: {deltaVal}"); + readErrorList.Add(item.Key, item.Value); + lgDebug($"--> 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; + } + } + + /// + /// Effettua lettura blocco memoria indicato e lo salva in copia locale HoldingRegisterLUT + /// + /// Indirizzo di aprtenza + /// NUm registri da leggere (max 120) + private void readBlockHoldingReg(int startAddr, int numReg, int baseAddr) + { + bool allOk = false; + if (currPLC.Connected) + { + allOk = true; + // fix massima lunghezza pacchetto + if (numReg > 120) + { + lgError($"Attenzione richiesta lettura blocco troppo grande: numReg {numReg} --> 120"); + numReg = 120; + } + // si lavora rispetto all'indirizzo base 40001 + + int[] rawData = new int[2]; + try + { + stopwatch.Restart(); + rawData = currPLC.ReadHoldingRegisters(startAddr - baseAddr, numReg); + stopwatch.Stop(); + lgTrace($"Lettura in blocco HoldingRegisters| startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {stopwatch.ElapsedMilliseconds}ms"); + // salvo in LUT la versione ESPLOSA 2 byte alla volta... + if (rawData.Length > 0) + { + for (int i = 0; i < rawData.Length / 2; i++) + { + int[] thisSet = new int[2]; + if (rawData.Length >= (i + 1) * 2) + { + Array.Copy(rawData, i * 2, thisSet, 0, 2); + // salvo nel registro... + int currAddr = startAddr + i * 2; + if (HoldingRegisterLUT.ContainsKey(currAddr)) + { + HoldingRegisterLUT[currAddr] = thisSet; + } + else + { + HoldingRegisterLUT.Add(currAddr, thisSet); + } + } + else + { + currReadErrors++; + allOk = false; + lgError($"Impossibile copiare dati, array di origine troppo corto: rawData.lenght: {rawData.Length} | base addr richiesto i*2: {i * 2} | errori currReadErrors: {currReadErrors}"); + } + } + } + } + catch (Exception exc) + { + currReadErrors++; + allOk = false; + lgError($"Eccezione in readBlockHoldingReg | currReadErrors: {currReadErrors}{Environment.NewLine}{exc}"); + } + } + else + { + lgError($"Attenzione, plc disconnesso... currReadErrors: {currReadErrors}"); + tryDisconnect(); + } + + // se tutto ok --> riduco contatore errori di 1... + if (allOk) + { + currReadErrors = currReadErrors >= 1 ? currReadErrors-- : 0; + } + else + { + // se > max errori --> disconnetto + if (currReadErrors > maxReadErrors) + { + lgError($"Superato limite errori Read ({currReadErrors}) --> tryDisconnect"); + currReadErrors = 0; + tryDisconnect(); + } + else + { + // altrimenti pausa forzata + Thread.Sleep(300); + } + } + } + + #endregion Private Methods } } \ No newline at end of file diff --git a/IOB-WIN-NEXT/IobModbusTCPHelpi.cs b/IOB-WIN-NEXT/IobModbusTCPHelpi.cs index c3022111..40596d8c 100644 --- a/IOB-WIN-NEXT/IobModbusTCPHelpi.cs +++ b/IOB-WIN-NEXT/IobModbusTCPHelpi.cs @@ -185,7 +185,14 @@ namespace IOB_WIN_NEXT get { int answ = 0; - + // hard coded + int statusReg = 41108; + if (HoldingRegisterLUT.ContainsKey(statusReg)) + { + int[] listInt = new int[2]; + listInt = HoldingRegisterLUT[statusReg]; + answ = ModbusClient.ConvertRegistersToInt(listInt); + } return answ; } } @@ -199,11 +206,13 @@ namespace IOB_WIN_NEXT { bool answ = false; int currStatus = 0; + // hard coded + int statusReg = 41094; // deve avere allarmi (è un allarme EStop) if (hasAlarms) { int[] listInt = new int[2]; - listInt = HoldingRegisterLUT[41094]; + listInt = HoldingRegisterLUT[statusReg]; currStatus = ModbusClient.ConvertRegistersToInt(listInt); // hard coded il 5° bit answ = ((currStatus & (1 << 4)) > 0);