using IOB_UT; using MapoSDK; using System; using System.Collections.Generic; using System.Net.NetworkInformation; using System.Text; using System.Threading; namespace IOB_WIN { public class IobOmron : IobGeneric { #region Private Fields /// /// LookUpTable di decodifica da CNC a segnali tipo bitmap MAPO /// private Dictionary signLUT = new Dictionary(); #endregion Private Fields #region Protected Fields /// /// Array valori letti da memoria CIO INGRESSI /// protected short[] memReadCIO_IN; /// /// Array valori letti da memoria CIO USCITE /// protected short[] memReadCIO_OUT; /// /// Array valori letti da memoria DM /// protected short[] memReadDM; /// /// Array valori letti da memoria WR /// protected short[] memReadWR; /// /// Array valori SCRITTI su memoria CIO /// protected short[] memWriteCIO; /// /// Array valori SCRITTI su memoria DM /// protected short[] memWriteDM; /// /// Array valori SCRITTI su memoria WR /// protected short[] memWriteWR; /// /// Oggetto MAIN x connessione OMRON /// protected OmronFinsTCP.Net.EtherNetPLC OMRON_ref; /// /// Array delle risposte dal controllo OMRON /// protected System.Collections.ArrayList resDataArray; #endregion Protected Fields #region Public Constructors /// /// estende l'init della classe base... /// /// /// public IobOmron(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { // gestione invio ritardato contapezzi pzCountDelay = utils.CRI("pzCountDelay"); lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // imposto i parametri PLC setParamPlc(); } #endregion Public Constructors #region Protected Properties /// /// Lettura scrittura commessa da OMRON (come array di coppie di byte) /// protected string commessa { get { string answ = ""; short[] response; byte[] byteData = new byte[20]; try { // leggo OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.WR, 0, 10, out response); // copio come byte... Buffer.BlockCopy(response, 0, byteData, 0, response.Length); answ = Encoding.ASCII.GetString(byteData); } catch { } return answ; } set { // converto in byte la mia stringa byte[] valByte = Encoding.ASCII.GetBytes(value); // limite 20 char... imposto a 20! int maxChar = valByte.Length < 20 ? valByte.Length : 20; // creao un array di 10 short MAX short[] valoriWord = new short[10]; // copio i valori Buffer.BlockCopy(valByte, 0, valoriWord, 0, maxChar); // scrivo su OMRON! OMRON_ref.WriteWords(OmronFinsTCP.Net.PlcMemory.WR, 0, 10, valoriWord); } } /// /// Verifico se abbia ALMENO un errore... /// protected bool hasError { get { bool answ = false; if (memReadCIO_OUT != null) { answ = ((memReadCIO_OUT[4] & (1 << 7)) != 0); } #if false bool answ = false; // controllo primi errori, parto dal primo e poi OR answ = answ || ((memReadCIO[0] & (1 << 7)) == 1); answ = answ || ((memReadCIO[1] & (1 << 0)) == 1); answ = answ || ((memReadCIO[1] & (1 << 1)) == 1); answ = answ || ((memReadCIO[1] & (1 << 3)) == 1); answ = answ || (memReadCIO[2] > 0); answ = answ || ((memReadCIO[3] & (1 << 0)) == 1); answ = answ || ((memReadCIO[3] & (1 << 1)) == 1); #endif return answ; } } /// /// Oggetto get/set per lettura (DM22-23) /scrittura (DM26-27) PESO RICHIESTO /// protected int pesoRichiesto { get { int answ = 0; try { short[] valDM; OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 22, 2, out valDM); answ = convDHD(valDM[0]) + 10000 * convDHD(valDM[1]); } catch { } return answ; } set { short[] valDM = intToShort(value); OMRON_ref.WriteWords(OmronFinsTCP.Net.PlcMemory.DM, 26, 2, valDM); // ora devo "comandare scrittura" su OMRON... // sollevo bit 65.0 x azzeramento OMRON_ref.WriteWord(OmronFinsTCP.Net.PlcMemory.CIO, 65, 1); Thread.Sleep(500); // ora sollevo bit e 65.1 x indicare nuovo valore... OMRON_ref.WriteWord(OmronFinsTCP.Net.PlcMemory.CIO, 65, 2); // ora attendo ed abbasso tutti i bit Thread.Sleep(500); OMRON_ref.WriteWord(OmronFinsTCP.Net.PlcMemory.CIO, 65, 0); } } /// /// Oggetto per lettura PESO rilevato (DM20-21) /// protected int pesoRilevato { get { int answ = 0; try { short[] valDM; OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 20, 2, out valDM); // legge delle coppie di valori INT, vanno trasformati in HEX e POI accodati, dove il primo è x 1 e il secondo x 10000 (in pratica va in testa) // esempio 12540 --> diviso in 1 | 2540 --> in HEX diventa 1 | 9472, ma il 9472 va su byte[0] e 1 su byte[1] answ = convDHD(valDM[0]) + 10000 * convDHD(valDM[1]); } catch { } return answ; } } /// /// Oggetto get/set x portata /// protected int portata { get { int answ = 0; try { short valDM; OMRON_ref.ReadWord(OmronFinsTCP.Net.PlcMemory.DM, 50, out valDM); answ = convDHD(valDM); } catch { } return answ; } set { // scrivo portata su memoria DM50... short portata = (short)convHD("0x" + value.ToString("0000")); OMRON_ref.WriteWord(OmronFinsTCP.Net.PlcMemory.DM, 50, portata); } } /// /// Oggetto get/set x quantità richiesta LOTTO /// protected int quantitaLotto { get { int answ = 0; try { short[] valDM; OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 52, 2, out valDM); answ = convDHD(valDM[0]) + 10000 * convDHD(valDM[1]); } catch { } return answ; } set { short[] valDM = intToShort(value); OMRON_ref.WriteWords(OmronFinsTCP.Net.PlcMemory.DM, 52, 2, valDM); } } #endregion Protected Properties #region Private Methods /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO /// private void decodeToBaseBitmap() { // init a zero... B_input = 0; /* ----------------------------------------------------- * bitmap MAPO * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * B4: manuale * B5: carico SILOS * B6: carico AUTOBOTTE ----------------------------------------------------- */ // bit 0 (poweron) imposto a 1 SE pingo... B_input = testPingMachine == IPStatus.Success ? 1 : 0; bool caricoSilos = ((memReadCIO_IN[55] & (1 << 2)) != 0); bool caricoAutobotte = ((memReadCIO_IN[50] & (1 << 2)) != 0); // filtro DEVE ANDARE ma VIENE RILEVATO DOPO, POTREBEB andare da solo x scaricare... bool runFiltro = ((memReadCIO_IN[0] & (1 << 1)) != 0); // RUN se CIO_bit 0.01 è RUN FILTRO... if ((caricoAutobotte || caricoSilos)) { // SE HO carico silos OPPURE carico autobotte --> RUN B_input += (1 << 1); } // ERROR generale (CORREGGERE!) if (hasError) { B_input += (1 << 3); } // carico SILOS if (caricoSilos) { B_input += (1 << 5); } // carico AUTOBOTTE if (caricoAutobotte) { B_input += (1 << 6); } // procedo SOLO SE è enabled IOB if (IobOnline) { try { currODL = utils.callUrl(urlGetCurrODL); // solo SE HO un ODL... if (string.IsNullOrEmpty(currODL) || currODL == "0") { if (periodicLog) { lgInfo(string.Format("OMRON | Lettura ODL andata a vuoto: currODL: {0}", currODL)); } } else { // se variato o scaduto timeout log... if (periodicLog || (currIdxODL.ToString() != currODL)) { lgInfo(string.Format("OMRON | Lettura ODL, currODL: {0} --> currIdxODL prec: {1}", currODL, currIdxODL)); } // provo a salvare nuovo ODL int.TryParse(currODL, out currIdxODL); } } catch (Exception exc) { if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); lastWarnODL = DateTime.Now; } } } else { // imposto currODL a vuoto! currODL = ""; if (periodicLog) { lgInfo($"Omron | Lettura ODL non effettuata: IobOnline: {IobOnline} | currODL impostato a vuoto"); } } // log opzionale! if (verboseLog) { lgInfo(string.Format("Trasformazione B_input: {0}", B_input)); } } /// /// Vera connessione ad OMRON /// /// private short doConnect() { // avvio un oggetto di comunicazione OMRON OMRON_ref = new OmronFinsTCP.Net.EtherNetPLC(); short port = 9600; short.TryParse(cIobConf.cncPort, out port); lgInfo($"Chiamata apertura OMRON FINS: {cIobConf.cncIpAddr}:{port}"); short esitoLink = OMRON_ref.Link(cIobConf.cncIpAddr, port, 500); return esitoLink; } /// /// Test completo funzioni OMRON /// private void omronWriteTest() { commessa = "OMRON EDF TEST"; // scrivo portata portata = 444; // scrivo pesatura richiesta pesoRichiesto = 22222; // scrivo DM 52-53 del lotto richiesto quantitaLotto = 66666; } /// /// Lettura e log valori x debug /// private void readAndLog() { OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.CIO, 0, 8, out memReadCIO_IN); OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.CIO, 100, 8, out memReadCIO_OUT); OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 0, 8, out memReadDM); OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.WR, 0, 8, out memReadWR); if (isVerboseLog) { lgInfo("Effettuata lettura dati CIO"); foreach (var item in memReadCIO_IN) { lgInfo($"Valori: {item} --> {baseUtils.binaryForm(item)}"); } lgInfo("Effettuata lettura dati DM"); foreach (var item in memReadDM) { lgInfo($"Valori: {item} --> {baseUtils.binaryForm(item)}"); } lgInfo("Effettuata lettura dati WR"); foreach (var item in memReadWR) { lgInfo($"Valori: {item} --> {baseUtils.binaryForm(item)}"); } } short[] respDM20; short[] respDM22; OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 20, 2, out respDM20); OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 22, 2, out respDM22); if (isVerboseLog) { // legge delle coppie di valori INT, vanno trasformati in HEX e POI accodati, dove il primo è x 1 e il secondo x 10000 (in pratica va in testa) lgInfo("Effettuata lettura dati respDM20"); foreach (var item in respDM20) { lgInfo($"Valori: {item} --> {baseUtils.binaryForm(item)}"); } lgInfo("Effettuata lettura dati respDM22"); foreach (var item in respDM22) { lgInfo($"Valori: {item} --> {baseUtils.binaryForm(item)}"); } } } #endregion Private Methods #region Protected Methods /// /// Calcola la conversione da byte --> num decimale --> HEX --> conversione come stringa in INT /// /// /// protected static int convDHD(short valore) { // legge delle coppie di valori INT, vanno trasformati in HEX e POI accodati, dove il primo è x 1 e il secondo x 10000 (in pratica va in testa) // esempio 12540 --> diviso in 1 | 2540 --> in HEX diventa 1 | 9472, ma il 9472 va su byte[0] e 1 su byte[1] int answ = 0; string hexVal = valore.ToString("x"); int.TryParse(hexVal, out answ); return answ; } /// /// Converte un valore di 2 short in un unico numero accostato: /// es: legge delle coppie di valori INT, vanno trasformati in HEX e POI accodati, dove il primo è x 1 e il secondo x 10000 (in pratica va in testa) /// 12540 --> diviso in 1 | 2540 --> in HEX diventa 1 | 9472, ma il 9472 va su byte[0] e 1 su byte[1] /// /// /// protected static int convFromHex(short[] valori) { int answ = 0; string fullVal = $"{convDHD(valori[1]).ToString("D4")}{convDHD(valori[0]).ToString("D4")}"; int.TryParse(fullVal, out answ); return answ; } /// /// Calcola la conversione da INTERO a intero HEX x OMRON /// /// /// protected static int convHD(string hexVal) { int answ = 0; if (!string.IsNullOrEmpty(hexVal)) { try { if (!hexVal.StartsWith("0x")) { hexVal = "0x" + hexVal; } answ = Convert.ToInt32(hexVal, 16); } catch { } } return answ; } /// /// Converte un valore INT in un array di due SHORT valido x scrivere su OMRON /// /// /// protected static short[] intToShort(int value) { short[] valDM = new short[2]; short highVal = (short)(value / 10000); short lowVal = (short)(value - highVal * 10000); valDM[0] = (short)convHD("0x" + lowVal.ToString("0000")); valDM[1] = (short)convHD("0x" + highVal.ToString("0000")); return valDM; } /// /// OVerride metodo x scrittura parametri su PLC /// /// protected override void plcWriteParams(List updatedPar) { int valInt = 0; if (updatedPar != null) { // controllo i parametri... ne gestisco 4... foreach (var item in updatedPar) { lgInfo($"Richiesti processing plcWriteParams per {item.name} | valore richiesto {item.reqValue} | valore attuale {item.value}"); string readBackVal = ""; switch (item.uid) { case "kgRich": int.TryParse(item.value, out valInt); pesoRichiesto = valInt; readBackVal = pesoRichiesto.ToString(); break; case "kgLotto": int.TryParse(item.value, out valInt); quantitaLotto = valInt; readBackVal = quantitaLotto.ToString(); break; case "portata": int.TryParse(item.value, out valInt); portata = valInt; readBackVal = portata.ToString(); break; case "setComm": commessa = item.value; readBackVal = commessa; break; default: break; } // SE non corrispondessero LOGGO ERRORE... if (item.value != readBackVal) { lgError($"Errore in plcWriteParams: uid {item.uid} | Wrote {item.value} | ReadBack {readBackVal}"); } } } } #endregion Protected Methods #region Public Methods /// /// Processo i task richiesti e li elimino dalla coda 1:1 /// /// public override Dictionary executeTasks(Dictionary task2exe) { // uso metodo base x ora return base.executeTasks(task2exe); } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); outVal.Add("kgAct", pesoRilevato.ToString()); outVal.Add("kgImp", pesoRichiesto.ToString()); return outVal; } /// /// Effettua vero processing contapezzi /// public override void processContapezzi() { if (utils.CRB("enableContapezzi")) { #if false try { // hard coded... !!!FARE!!! rivedere megio conf contapezziPLC = pzCounter; // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con indicazione area) if (cIobConf.optPar.Count > 0 && getOptPar("PZCOUNT_MODE") != "") { } } catch (Exception exc) { lgError(exc, "Errore in contapezzi OMRON"); } #endif } } /// /// Effettua lettura semafori principale /// Parametri da aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { base.readSemafori(ref currDispData); try { if (verboseLog) { lgInfo("inizio read semafori"); } currDispData.semIn = Semaforo.SV; // effettuo TUTTE le letture OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.CIO, 0, 60, out memReadCIO_IN); OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.CIO, 100, 10, out memReadCIO_OUT); //OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.DM, 0, 8, out memReadDM); //OMRON_ref.ReadWords(OmronFinsTCP.Net.PlcMemory.WR, 0, 8, out memReadWR); contapezziPLC = pesoRilevato; // decodifica e gestione decodeToBaseBitmap(); reportRawInput(ref currDispData); } catch { currDispData.semIn = Semaforo.SR; } } /// /// Effettua reset del contapezzi, in questo caso il conteggio dei KG /// /// public override bool resetcontapezziPLC() { bool answ = false; #if false // ...SE abilitato da conf IOB if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE") { // scrivo valore 0 x il contapezzi try { pzCounter = 0; } catch (Exception exc) { lgError(exc, "Errore in RESET contapezzi OMRON"); connectionOk = false; } answ = true; } else { lgError("Impossibile effettuare RESET contapezzi OMRON, mancanza parametro OPT:ENABLE_PZ_RESET"); } #endif return answ; } /// /// Effettua IMPOSTAZIONE FORZATA del contapezzi, in questo caso il conteggio dei KG /// /// public override bool setcontapezziPLC(int newPzCount) { bool answ = false; #if false // ...SE abilitato da conf IOB if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE") { // scrivo valore 0 x il contapezzi try { pzCounter = newPzCount; } catch (Exception exc) { lgError(exc, "Errore in SET contapezzi OMRON"); connectionOk = false; } answ = true; } else { lgError("Impossibile effettuare SET contapezzi OMRON, mancanza parametro OPT:ENABLE_PZ_RESET"); } #endif return answ; } /// /// Override connessione /// public override void tryConnect() { if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("OMRON: ConnKO - tryConnect"); } // in primis salvo data ping... lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { string szStatusConnection = ""; try { // ora provo connessione... parentForm.commPlcActive = true; short esitoLink = doConnect(); lgInfo($"szStatusConnection OMRON, esitoLink: {esitoLink}"); parentForm.commPlcActive = false; // imposto i parametri... setParamPlc(); connectionOk = true; // refresh stato connessione!!! if (connectionOk) { if (adpRunning) { lgInfo("Connessione OK"); } } else { lgError("Impossibile procedere, connessione mancante..."); } } catch (Exception exc) { lgFatal($"Errore nella connessione all'adapter OMRON: {szStatusConnection}{Environment.NewLine}{exc}"); connectionOk = false; lgInfo($"Eccezione in TryConnect, Adapter OMRON NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione"); } } else { // loggo no risposta ping ... connectionOk = false; if (verboseLog || periodicLog) { lgInfo($"Attenzione: OMRON controllo PING fallito per IP {cIobConf.cncIpAddr}"); } } } } else { needRefresh = true; } // 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 { OMRON_ref.Close(); connectionOk = false; lgInfo(szStatusConnection); lgInfo("Effettuata disconnessione adapter OMRON!"); } catch (Exception exc) { lgFatal(exc, "Errore nella disconnessione dall'adapter OMRON"); } } else { lgError("IMPOSSIBILE effettuare disconnessione OMRON: Connessione non disponibile..."); } } #endregion Public Methods } }