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 HAM * - specifico comportamento impianti HAM Pizzaferri * * STRUTTURA MEMORIA a banchi di byte, convertiti successivamente in bit/int/real: * lettura: xxx byte, * scrittura yyy byte * G:\Drive condivisi\30_Clienti\Pizzaferri\Impianti\HAM * * * -------------------------------------------------------------------------------- */ public class IobModbusTCPHam : IobModbusTCP { #region Public Constructors /// Classe base con i metodi x ModBusTCP /// /// /// public IobModbusTCPHam(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { lgInfo("NEW IOB ModBus TCP HAM"); //testRead(); // provo lettura una prima volta i dati DYN if (currPLC.Connected) { try { processDynData(); } catch (Exception exc) { lgError($"Eccezione in processDynData iniziale x ModBus TCP HAM:{Environment.NewLine}{exc}"); } } } #endregion Public Constructors #region Protected Properties protected bool hasAlarms { get { bool answ = false; #if false int numErrors = 0; uint currStatus = 0; if (alarmMaps != null) { // leggo a ciclo le aree degli allarmi CONFIGURATI, se ne trovo --> segnalo allarme... foreach (var item in alarmMaps) { // in primis decremento eventuali blink... item.decreaseBlinkCounter(); // banchi in WORD (2 byte) --> scompongo for (int i = 0; i < item.size / 2; i++) { currStatus = S7.Net.Types.Counter.FromByteArray(RawInput.Skip(item.index + 2 * i).Take(2).ToArray()); // verifico SE sia variato... if (item.isChanged(i, currStatus)) { numErrors++; answ = true; // registro gli allarmi attivi e trasmetto... if (sendAlarmVariations(item.memAddr, i, item.alarmsState[i], currStatus)) { // se inviato --> salvo stato da current... item.updStatusVal(i, currStatus); } } } } } #endif return answ; } } #endregion Protected Properties #region Private Methods private void testRead() { //Ip-Address and Port of Modbus-TCP-Server //currPLC = new ModbusClient(cIobConf.cncIpAddr, 502); ////Connect to Server //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 = currPLC.ReadHoldingRegisters(0, 34); //Read 10 Holding Registers from Server, starting with Address 1 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++) // Console.WriteLine("Value of Coil " + (9 + i + 1) + " " + readCoils[i].ToString()); for (int i = 0; i < readHR1000.Length / 2; i++) { Console.WriteLine($"Value of HoldingRegister {(i)} | {readHR1000[i]} / {readHR1000[i + 1]}"); int[] thisSet = new int[2]; Array.Copy(readHR1000, i, thisSet, 0, 2); Console.WriteLine($"Convert val HoldingRegister {(i)} | {ModbusClient.ConvertRegistersToFloat(thisSet)}"); } //Console.Write("Press any key to continue . . . "); //Console.ReadKey(true); } #endregion Private Methods #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 override void decodeToBaseBitmap() { // init a zero... B_input = 0; /* ----------------------------------------------------- * bitmap MAPO STANDARD * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * ----------------------------------------------------- */ var MemInt = new byte[2]; int byteSignals = 0; // bit 0 (poweron) imposto a 1 SE connected... if (currPLC.Connected) { byteSignals += (1 << 0); } // processo dagli stati + gravi... if (hasAlarms) { byteSignals += (1 << 3); } else { byteSignals += (1 << 1); } // salvo! B_input = byteSignals; } /// /// effettua il setup dei memblock da gestire (NON leggo intera memoria ma tanti blocchi...) /// protected override void setupMemBlocks() { // da calcolare... ora setup cablato... memSetR.Add(1, 34); } #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 HAM: {task2exe.Count} task ricevuti"); // Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti... Dictionary taskDone = new Dictionary(); bool taskOk = false; string taskVal = ""; // inizio con 1 byte di default byte[] MemBlock = new byte[1]; string memAddrWrite = ""; if (task2exe != null) { #if false // cerco task specifici foreach (var item in task2exe) { taskOk = false; taskVal = ""; // converto richiesta in enum... taskType tName = taskType.nihil; Enum.TryParse(item.Key, out tName); // controllo sulla KEY switch (tName) { case taskType.nihil: case taskType.fixStopSetup: case taskType.forceResetPzCount: case taskType.forceSetPzCount: case taskType.setProg: case taskType.sendWatchDogMes2Plc: taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; break; case taskType.setPzComm: case taskType.setArt: case taskType.setComm: saveProdData(item); int byteSize = 0; // recupero dati da memMap... altrimenti NULLA if (memMap.mMapWrite.ContainsKey(item.Key)) { dataConf currMem = memMap.mMapWrite[item.Key]; byteSize = currMem.size; memAddrWrite = currMem.memAddr; MemBlock = new byte[byteSize]; if (currMem.tipoMem == plcDataType.String) { saveStringOnMemBlock(ref MemBlock, item.Key, 0, byteSize); } else if (currMem.tipoMem == plcDataType.DInt) { int valDInt = 0; int.TryParse(item.Value, out valDInt); MemBlock = S7.Net.Types.DInt.ToByteArray(valDInt); } else if (currMem.tipoMem == plcDataType.Int) { short valInt = 0; short.TryParse(item.Value, out valInt); MemBlock = S7.Net.Types.Int.ToByteArray(valInt); } } else { lgError($"Errore: non trovata chiave write in memMap.mMapWrite per {item.Key}"); } taskVal = item.Value; break; case taskType.startSetup: // processo scrittura BIT su DB6.DBDW216 MemBlock = new byte[1]; MemBlock[0] = (byte)1; memAddrWrite = "DB6.DBDW216"; break; case taskType.stopSetup: // processo scrittura BIT su DB6.DBDW216 MemBlock = new byte[1]; MemBlock[0] = (byte)0; memAddrWrite = "DB6.DBDW216"; break; case taskType.setParameter: // richiedo da URL i parametri WRITE da popolare lgInfo("Chiamata processMemWriteRequests"); taskVal = processMemWriteRequests(); // se restituiscce "" faccio altra prova... if (string.IsNullOrEmpty(taskVal)) { // i parametri me li aspetto come stringa composta paramName|paramvalue if (item.Value.Contains("|")) { string[] paramsJob = item.Value.Split('|'); taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; } else { taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value"; } } break; default: taskVal = "SKIPPED | NO EXEC"; break; } // aggiungo task! taskDone.Add(item.Key, taskVal); if (!string.IsNullOrEmpty(memAddrWrite)) { // scrivo! taskOk = S7WriteBB(ref MemBlock, memAddrWrite); } if (!taskOk) { lgError($"Errore in S7WriteBB durante executeTasks: {item.Key} | {item.Value}"); } } #endif } return taskDone; } /// /// Override connessione /// public override void tryConnect() { bool doLog = (verboseLog || periodicLog); lgInfo("ModBus TCP HAM: tryConnect step 01"); if (!connectionOk) { // SE è necessario refresh... if (needRefresh) { lgInfo("ModBus TCP HAM: tryConnect step 02"); // reimporto parametri PLC se necessario... setParamPlc(); } lgInfo("ModBus TCP HAM: 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 HAM: ConnKO - tryConnect"); } lgInfo("ModBus TCP HAM: 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 } }