Vaas98 Inserito: 14 gennaio 2021 Segnala Share Inserito: 14 gennaio 2021 Buongiorno, Sto cercando di connettermi a un plc Omron-Fins con uno script in python. Ho trovato su internet una libreria che mi permette di connettermi con successo al plc e anche di leggere qualcosa, purtroppo incomprensibile (b'\xc0\x00\x02\x00\x1e\x00\x00\xd3\x00`\x01\x01\x00\x00\x00\x00'). Per ottenere tale risultato ho utilizzato questo codice : import fins.udp import time fins_instance = fins.udp.UDPFinsConnection() fins_instance.connect('192.168.1.211') fins_instance.dest_node_add=211 fins_instance.srce_node_add=30 mem_area = fins_instance.memory_area_read(fins.FinsPLCMemoryAreas().CIO_WORD,b'\x00\x51\x06') print(mem_area) Purtroppo penso di aver sbagliato le configurazioni in quanto non conoscendo bene i plc omron-fins non so cosa io debba mettere in dest_node e in srce_node. Qualcuno sa dirmi qualcosa in più ? Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 14 gennaio 2021 Segnala Share Inserita: 14 gennaio 2021 (modificato) Se ti può servire ti posso girare il codice di una implementazione che ho realizzato in C++/Qt, la uso con i CP1 etc... Potresti cercare di portartelo su python leggendoti il codice. Nei manuali alcune cose se vuoi implementarlo sono un po' fuorvianti e legate a vecchie implementazioni, a me spiegò molte cose direttamente un ingegnere di OMRON Milano. È un protocollo abbastanza semplice se lo usi solo per scambiare dati. Modificato: 14 gennaio 2021 da Marco Mondin Link al commento Condividi su altri siti More sharing options...
Vaas98 Inserita: 15 gennaio 2021 Autore Segnala Share Inserita: 15 gennaio 2021 Quote Se ti può servire ti posso girare il codice di una implementazione che ho realizzato in C++/Qt, la uso con i CP1 etc... Potresti cercare di portartelo su python leggendoti il codice.Nei manuali alcune cose se vuoi implementarlo sono un po' fuorvianti e legate a vecchie implementazioni, a me spiegò molte cose direttamente un ingegnere di OMRON Milano. È un protocollo abbastanza semplice se lo usi solo per scambiare dati. Se puoi mi saresti molto di aiuto. Grazie mille Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 15 gennaio 2021 Segnala Share Inserita: 15 gennaio 2021 Eccolo. Lo metto qua. Mai servisse ad altri. È LGPL, quindi linkabile dinamicamente a applicativi closed. È il codice grezzo della libreria. Se ti serve un esempio su come funziona e come interagiscono tra loro gli oggetti ti chiedo di avere un po' di pazienza in quanto devo preparartene uno. https://drive.google.com/file/d/15ugA9uLOwilb1tKjnEhfi51xiIszOERo/view?usp=sharing Ha un oggetto per la gestione del "socket" (che non sono socket essendo UDP) FINS QFinsUdpSocket. Ha un oggetto QFinsDataUnit, per comporre le richieste e con cui ricevere le risposte. (Funziona sulla falsa riga del QModbusDataUnit che esiste nelle librerie Qt) Ha un oggetto QFinsReply, che viene rilasciato dal socket ad ogni richiesta e che emette un signal quando la risposta dal device è arrivata. La reply possiede una QFinsDataUnit con i dati ricevuti. Ha giù tutta una serie di method per la gestione dei tipi di dato comprese stringa. Converte già tutto dal formato OMRON a quello PC e viceversa, senza dover pensare a gestire nulla di raw. Link al commento Condividi su altri siti More sharing options...
Vaas98 Inserita: 15 gennaio 2021 Autore Segnala Share Inserita: 15 gennaio 2021 (modificato) Grazie mille ho già iniziato a dargli una occhiata, ti posso chiedere comunque, se hai il tempo, un piccolo esempio del funzionamento. Veramente gentile. Posso chiederti anche un altra cosa....in che classe fai la "traduzione" dei byte che leggi da fins Modificato: 15 gennaio 2021 da Vaas98 Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 15 gennaio 2021 Segnala Share Inserita: 15 gennaio 2021 2 ore fa, Vaas98 ha scritto: Grazie mille ho già iniziato a dargli una occhiata, ti posso chiedere comunque, se hai il tempo, un piccolo esempio del funzionamento. Veramente gentile. Posso chiederti anche un altra cosa....in che classe fai la "traduzione" dei byte che leggi da fins È passato molto tempo da quando lo scrissi. Mi pare che la lettura cominci sul metodo setRawPacket del QFinsDataUnit, per poi passare alla QFinsReply etc... Il QFinsDataMapper permette di fare il lavoro sporco del mappare i dati raw da vector di WORDS ai dati leggibili. Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 16 gennaio 2021 Segnala Share Inserita: 16 gennaio 2021 Eccomi: Cominciamo... Io uso un QFinsDataMapper, lo ho creato per togliermi tanto lavoro sporco. Si smazza tutta la roba RAW lui. Questo è il costruttore della classe che uso per uno specifico "Cabinet", macchina da monitorare: Cabinet1::Cabinet1(QObject *parent) : Cabinet(parent), finsDataMapper(new QFinsDataMapper(this)), watchDogTimer(new QTimer(this)), oldWatchDog(0), numAlarms(16) { cleanData(); setAlarmList(); setWarningList(); QSettings setup; setId(1); setMethods(); setup.beginGroup(QString("MACHINE").append(QString::number(id()))); setName(setup.value("NAME").toString()); //Imposto i parametri di connessione alla macchina //Nel mio caso li pesco da un file ".ini" con il QSettings quint8 dna = quint8(setup.value("DNA").toUInt()); quint8 da1 = quint8(setup.value("DA1").toUInt()); quint8 da2 = quint8(setup.value("DA2").toUInt()); quint8 sna = quint8(setup.value("SNA").toUInt()); quint8 sa1 = quint8(setup.value("SA1").toUInt()); quint8 sa2 = quint8(setup.value("SA2").toUInt()); QHostAddress local = QHostAddress(setup.value("LISTEN_IP").toString()); QHostAddress plc = QHostAddress(setup.value("IP").toString()); quint16 port = quint16(setup.value("LISTEN_PORT").toUInt()); //Imposto un QFinsDataMapper finsDataMapper->bind(local, port, plc, dna, da1, da2, sna, sa1, sa2, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); setup.endGroup(); watchDogTimer->setSingleShot(true); //Collego i segnali alle rispettive callaback connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(watchDogCaptured())); connect(finsDataMapper, SIGNAL(readDone(int)), this, SLOT(checkReceived(int))); connect(finsDataMapper, SIGNAL(readFailure(int)), this, SLOT(checkFailureReceived(int))); connect(finsDataMapper, SIGNAL(writeDone(int)), this, SLOT(checkWrite(int))); connect(finsDataMapper, SIGNAL(writeFailure(int)), this, SLOT(checkFailureWrite(int))); mapData(); watchDogTimer->start(TM_C1_DISCONNECT); } Qua mappo i dati da monitorare, ne definisco i tipi nomi mnemonici etc...: void Cabinet1::mapData() { //Tutte le variabili sono semplici INT a cui appoggio un ID del datamapper. //Nel datamapper posso mettere range di dati da monitorare/leggere/scrivere h0_alarms = finsDataMapper->addDataRange(QFins::AreaHR, 0, 1); //Definisco nomi di comodo mnemonici per specifiche variabili e ne dichiaro il tipo. finsDataMapper->mapData("alarms", QFins::Word, QFins::AreaHR, 0); c0_inputs1 = finsDataMapper->addDataRange(QFins::AreaCIO, 0, 1); finsDataMapper->mapData("inputs1", QFins::Word, QFins::AreaCIO, 0); w0_10_vars = finsDataMapper->addDataRange(QFins::AreaWR, 0, 10); finsDataMapper->mapData("phase", QFins::Word, QFins::AreaWR, 0); finsDataMapper->mapData("autoOn", QFins::Word, QFins::AreaWR, 2); d100_d250 = finsDataMapper->addDataRange(QFins::AreaDM, 100, 150); finsDataMapper->mapData("setWashing", QFins::UInt, QFins::AreaDM, 100); finsDataMapper->mapData("setSteam", QFins::UInt, QFins::AreaDM, 101); finsDataMapper->mapData("setCondensation", QFins::UInt, QFins::AreaDM, 102); finsDataMapper->mapData("cycleTx", QFins::UInt, QFins::AreaDM, 150); finsDataMapper->mapData("cycleActive", QFins::UInt, QFins::AreaDM, 160); finsDataMapper->mapData("readWashing", QFins::UInt, QFins::AreaDM, 200); finsDataMapper->mapData("readSteam", QFins::UInt, QFins::AreaDM, 201); finsDataMapper->mapData("readCondensation", QFins::UInt, QFins::AreaDM, 202); d704 = finsDataMapper->addDataRange(QFins::AreaDM, 700, 10); finsDataMapper->mapData("cycleCounter", QFins::UDInt, QFins::AreaDM, 700); d1000_watchDog = finsDataMapper->addDataRange(QFins::AreaDM, 1000, 1); finsDataMapper->mapData("watchdog", QFins::UInt, QFins::AreaDM, 1000); //Qua dichiaro alcuni dati che voglio che il datamapper controlli in modo ciclico //Emetterà un segnale per indicarmi se uno di questi è cambiato //Gli altri li lascio in gestione manuale su richiesta finsDataMapper->addCyclicDataRange(d1000_watchDog); finsDataMapper->addCyclicDataRange(h0_alarms); finsDataMapper->addCyclicDataRange(c0_inputs1); finsDataMapper->addCyclicDataRange(w0_10_vars); finsDataMapper->addCyclicDataRange(d100_d250); finsDataMapper->addCyclicDataRange(d704); finsDataMapper->setCyclicTime(); //Ancora un paio di mappature productCode = finsDataMapper->mapData("productCode", QFins::String, QFins::AreaDM, 1500, 64); lotNumber = finsDataMapper->mapData("lotNumber", QFins::ULInt, QFins::AreaDM, 2000, 1); //Il segnale cyclicDataChanged, mi restituirà l'ID del range che ha subito un cambio connect(finsDataMapper, SIGNAL(cyclicDataChanged(int)), this, SLOT(checkDataChanged(int))); } Qua faccio i controlli sull'evento ciclico di dato cambiato: void Cabinet1::checkDataChanged(int index) { if (index == h0_alarms) { checkAlarms(); } if (index == c0_inputs1) { checkInputs(); } if (index == w0_10_vars) { checkWords(); } if (index == d100_d250) { checkData(); } if (index == d704) { checkData704(); } if (index == d1000_watchDog) { int watchDog = finsDataMapper->value("watchdog").toInt(); if (oldWatchDog != watchDog) { oldWatchDog = watchDog; if (!m_connected) { m_connected = true; emit connected(); checkAlarms(); sendLot(); sendProduction(); } watchDogTimer->start(TM_C1_DISCONNECT); finsDataMapper->setValue("watchdog", watchDog + 1); finsDataMapper->syncToPlc(d1000_watchDog); } } } Questo è un esempio di scrittura asincrona (Nel mio caso quando da un ERP mi mandano un lotto lo scrivo in macchina). Vedi sopra come ho mappato il dato: void Cabinet1::sendLot() { finsDataMapper->setValue("lotNumber", lot); finsDataMapper->syncToPlc(lotNumber); } Questo è un esempio di lettura asincrona: //Leggo il dato autoOn e lo faccio diventare unsigned int del C/C++. quint16 word = quint16(finsDataMapper->value("autoOn").toUInt()); //Uso un metodo statico di comodo che mi estrae bit da una word // (NON MI UCCIDERE SE NON HO MESSO ENUM NON È DA ME) m_run = QFinsDataMapper::bitValue(word, 0); word = quint16(finsDataMapper->value("phase").toUInt()); m_initPhase = QFinsDataMapper::bitValue(word, 0); m_phase1 = QFinsDataMapper::bitValue(word, 1); m_phase2 = QFinsDataMapper::bitValue(word, 2); m_phase3 = QFinsDataMapper::bitValue(word, 3); m_phase4 = QFinsDataMapper::bitValue(word, 4); m_endPhase = QFinsDataMapper::bitValue(word, 5); Altri esempi di lettura: void Cabinet1::checkData() { m_setWashing = finsDataMapper->value("setWashing").toInt(); m_setSteam = finsDataMapper->value("setSteam").toInt(); m_setCondensation = finsDataMapper->value("setCondensation").toInt(); m_cycleTx = finsDataMapper->value("cycleTx").toInt(); m_currentCycle = finsDataMapper->value("cycleActive").toInt(); m_getWashing = finsDataMapper->value("readWashing").toInt(); m_getSteam = finsDataMapper->value("readSteam").toInt(); m_getCondensation = finsDataMapper->value("readCondensation").toInt(); } void Cabinet1::checkData704() { m_counterCycle = finsDataMapper->value("cycleCounter").toInt(); } Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 16 gennaio 2021 Segnala Share Inserita: 16 gennaio 2021 Il 15/1/2021 alle 11:38 , Vaas98 ha scritto: Grazie mille ho già iniziato a dargli una occhiata, ti posso chiedere comunque, se hai il tempo, un piccolo esempio del funzionamento. Veramente gentile. Posso chiederti anche un altra cosa....in che classe fai la "traduzione" dei byte che leggi da fins L'esempio che ho postato è di molto tempo fa. Non usavo ancora cose come le lambda functions di C++11 e usavo ancora la notazione dei segnali delle librerie Qt4, mantenuta nelle Qt5 (anche se sarebbe stato meglio usare quella nuova). Oggi lo scriverei in modo molto diverso e più compatto. Link al commento Condividi su altri siti More sharing options...
Vaas98 Inserita: 18 gennaio 2021 Autore Segnala Share Inserita: 18 gennaio 2021 Il 16/1/2021 alle 14:54 , Marco Mondin ha scritto: L'esempio che ho postato è di molto tempo fa. Non usavo ancora cose come le lambda functions di C++11 e usavo ancora la notazione dei segnali delle librerie Qt4, mantenuta nelle Qt5 (anche se sarebbe stato meglio usare quella nuova). Oggi lo scriverei in modo molto diverso e più compatto. Grazie mille lo stesso Link al commento Condividi su altri siti More sharing options...
Vaas98 Inserita: 22 gennaio 2021 Autore Segnala Share Inserita: 22 gennaio 2021 Buongiorno, scusami se ti riscrivo dopo parecchio tempo ma ero impegnato con altro al lavoro e ho dovuto accantonare un attimo il discorso. Da ignorante in materia perchè la prima volta che comunque mi affaccio non solo a un plc omron ma a un plc in generale, dove tu imposti i parametri di configurazione .....giusto per intenderci dna,da1,da2,sna,sa1,sa2,local cosa ci devo inserire per configurarlo ? C'è quei valori cosa sono nella pratica/dove li riperisco sul plc Grazie in anticipo Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 22 gennaio 2021 Segnala Share Inserita: 22 gennaio 2021 (modificato) Sono i parametri che definiscono la rete fins. Sono legati soprattutto a quando FINS non era su UDP e TCP. Oggi con UDP perdono parte del loro significato. Potrebbero ancora essere utili per instradare pacchetti su reti ibride usando un PLC come gateway, tuttavia non credo sia molto probabile. DNA e SNA sono le reti sorgenti e di destinazione FINS per rete locale (link diretto) vanno sempre a 00 o 01 non ricordo ma mi pare che in UDP sia 01. DA1 e SA1 sono gli indirizzi FINS sorgente e di destinazione del nodo. DA1 mi pare che su UDP vada preso l'ultimo byte dell'indirizzo IP del plc, SA1 del PC o viceversa. DA2 e SA2 sono gli indirizzi FINS sorgente e di destinazione dell'unità sul nodo. In UDP prova a mettere 00 su DA2 e 00 su SA2 ignora il manuale FINS. Uso la mia libreria raramente, ci avrò fatto 5-6 applicazioni per adattare macchine ad un 4.0, alla fine metto quei parametri in un file .ini e li testo sul campo. Con UDP hanno valori che non si riferiscono ad un tubo con quelli dei manuali e i manuali sono fatti bene per la vecchia versione non ethernet, ma male per quella ethernet. Quei dati devono essere presenti nell'header di ogni pacchetto se non ci sono non funziona. Il SID è solo un contatore pacchetti per controllare a livello applicativi quanti ne perdi etc... (P.S. sei per caso di Milano?) Modificato: 22 gennaio 2021 da Marco Mondin Link al commento Condividi su altri siti More sharing options...
Vaas98 Inserita: 22 gennaio 2021 Autore Segnala Share Inserita: 22 gennaio 2021 (modificato) Appena il sito di qt decide di ripartire installo qt e inizio a testare comunque non sono proprio di Milano ma nelle zone limitrofe Modificato: 22 gennaio 2021 da Vaas98 Link al commento Condividi su altri siti More sharing options...
Marco Mondin Inserita: 22 gennaio 2021 Segnala Share Inserita: 22 gennaio 2021 (modificato) 1 ora fa, Vaas98 ha scritto: Appena il sito di qt decide di ripartire installo qt e inizio a testare comunque non sono proprio di Milano ma nelle zone limitrofe Ti consiglio Qt tra 5.9 e 5.12, lo ho testato su tutte, mentre non sono ancora passato a versioni più recenti e non posso garantire che compili correttamente. Puoi usare sia MSVC che MinGW per windows o sia gcc che clang per Linux, non fa grande differenza, solo su MSVC è un po' macchinoso configurare CDB per Qt, ma nulla di impossibile. Noi attualmente in produzione siamo a Qt 5.12.2 sia su Linux che su Windows che su MAC(Dove abbiamo solo un applicativo per il settore odontoiatrico). Prima di passare a versioni nuove dedichiamo sempre un po' di tempo ai test e cerchiamo solo di passare a versioni LTS. Con MSVC siamo fermi alla 2015 per via della compatibilità con Qt5.12.2 Per scopo personale su linux uso un po' tutte le versioni e sovente me le compilo a partire dai sorgenti per ottimizzare alcune cose. Modificato: 22 gennaio 2021 da Marco Mondin Link al commento Condividi su altri siti More sharing options...
GraGia Inserita: 21 marzo 2022 Segnala Share Inserita: 21 marzo 2022 Ciao, l'implementazione omron fins per python mi interessa, posso anche collaborare allo sviluppo. Link al commento Condividi su altri siti More sharing options...
Livio Orsini Inserita: 21 marzo 2022 Segnala Share Inserita: 21 marzo 2022 hai letto le date? l'ultimo messaggio risale ad oltre un anno fa! Link al commento Condividi su altri siti More sharing options...
Messaggi consigliati