Fiddle एक कम ज्ञात मॉड्यूल है जिसे Ruby की मानक लाइब्रेरी में 1.9.x में जोड़ा गया था। यह आपको Ruby से सीधे C लाइब्रेरी के साथ बातचीत करने की अनुमति देता है। यह आपको रनटाइम पर Ruby इंटरप्रेटर का निरीक्षण और परिवर्तन करने भी देता है।
यह libffi को रैप करके काम करता है, एक लोकप्रिय C लाइब्रेरी जो एक भाषा में लिखे गए कोड को दूसरी भाषा में लिखे गए मेथड्स को कॉल करने की अनुमति देती है। यदि आपने इसके बारे में नहीं सुना है, तो "ffi" का अर्थ "फॉरेन फंक्शन इंटरफेस" है। और आप केवल C तक सीमित नहीं हैं। एक बार जब आप Fiddle सीख लेते हैं, तो आप उन भाषाओं में लिखी गई लाइब्रेरी का उपयोग कर सकते हैं जो इसका समर्थन करती हैं।
आइए Fiddle पर एक नज़र डालें। हम एक सरल उदाहरण से शुरू करेंगे और फिर एक Arduino को सीरियल पोर्ट पर एक्सेस करके समाप्त करेंगे। आपको इतना C जानने की आवश्यकता नहीं है। मैं वादा करता हूँ। :)
एक सरल उदाहरण
सादे पुराने Ruby में, आपको हमेशा एक मेथड को कॉल करने से पहले परिभाषित करना होगा। Fiddle के साथ भी ऐसा ही है। आप सीधे C फ़ंक्शन को कॉल नहीं कर सकते। इसके बजाय आपको C फ़ंक्शन के लिए एक रैपर बनाना होगा, फिर रैपर को कॉल करना होगा।
नीचे दिए गए उदाहरण में, हम C के लॉगरिदम फ़ंक्शन को रैप कर रहे हैं। हम मूल रूप से Ruby के `Math.log` को डुप्लिकेट कर रहे हैं।
require 'fiddle' # हम एक लाइब्रेरी "खोलने" जा रहे हैं, इसलिए हमें Fiddle को बताना होगा # कि यह डिस्क पर कहाँ स्थित है। यह उदाहरण OSX Yosemite पर काम करता है। libm = Fiddle.dlopen('/usr/lib/libSystem.dylib') # C फ़ंक्शन "log" के लिए एक रैपर बनाएं। log = Fiddle::Function.new( libm['log'], # खोली गई मैथ लाइब्रेरी से फ़ंक्शन प्राप्त करें [Fiddle::TYPE_DOUBLE], # इसका एक आर्गुमेंट है, एक डबल, जो Ruby के Float के समान है Fiddle::TYPE_DOUBLE # यह एक डबल लौटाता है ) # हमारे रैपर के माध्यम से C फ़ंक्शन को कॉल करें puts log.call(3.14159)
इसे सुंदर बनाना
पिछला उदाहरण काम करता है, लेकिन यह थोड़ा वर्बोज़ है। मुझे यकीन है कि आप कल्पना कर सकते हैं कि यदि आपको 100 फ़ंक्शन रैप करने पड़े तो चीजें कितनी अव्यवस्थित हो जाएंगी। इसलिए Fiddle एक अच्छा DSL प्रदान करता है। यह `Fiddle::Importer` मिक्सिन के माध्यम से उजागर होता है।
इस मिक्सिन का उपयोग करके बाहरी फ़ंक्शन से भरा एक मॉड्यूल बनाना छोटा काम है। नीचे दिए गए उदाहरण में, हम एक मॉड्यूल बना रहे हैं जिसमें कई लॉगरिदम मेथड्स हैं।
require 'fiddle' require 'fiddle/import' module Logs extend Fiddle::Importer dlload '/usr/lib/libSystem.dylib' extern 'double log(double)' extern 'double log10(double)' extern 'double log2(double)' end # हम बाहरी फ़ंक्शन को कॉल कर सकते हैं जैसे वे Ruby मेथड्स हों! puts Logs.log(10) # 2.302585092994046 puts Logs.log10(10) # 1.0 puts Logs.log2(10) # 3.321928094887362
सीरियल पोर्ट को नियंत्रित करना
ठीक है, तो आपने अंततः उन Arduinos में से एक खरीदा जिसके बारे में आप वर्षों से सोच रहे थे। आपने सीरियल पोर्ट का उपयोग करके अपने कंप्यूटर पर प्रति सेकंड एक बार "हैलो वर्ल्ड" टेक्स्ट भेजा है।
अब, यह वास्तव में अच्छा होगा यदि आप Ruby का उपयोग करके उस डेटा को पढ़ सकें। और आप वास्तव में Ruby के मानक IO मेथड्स का उपयोग करके सीरियल पोर्ट से पढ़ सकते हैं। लेकिन ये IO मेथड्स हमें सीरियल इनपुट को उस स्तर की बारीकियों के साथ कॉन्फ़िगर नहीं करने देते जिसकी हमें आवश्यकता है यदि हम हार्डकोर हार्डवेयर हैकर्स बनने जा रहे हैं।
सौभाग्य से, C मानक लाइब्रेरी सीरियल पोर्ट के साथ काम करने के लिए आवश्यक सभी फ़ंक्शन प्रदान करती है। और Fiddle का उपयोग करके, हम इन C फ़ंक्शन को Ruby में एक्सेस कर सकते हैं।
बेशक, ऐसे जेम्स पहले से मौजूद हैं जो आपके लिए यह करते हैं। जब मैं इस कोड पर काम कर रहा था तो मैंने rubyserial जेम से थोड़ी प्रेरणा ली। लेकिन हमारा लक्ष्य यह सीखना है कि इस सामान को स्वयं कैसे करें।
termios.h की जांच करना
C में, .h में समाप्त होने वाला कोई भी फ़ाइलनाम एक हेडर फ़ाइल है। इसमें उन सभी फ़ंक्शंस और कॉन्स्टेंट्स की सूची होती है जो लाइब्रेरी थर्ड-पार्टी कोड (हमारा कोड) के लिए उपलब्ध कराती है। किसी भी C लाइब्रेरी को रैप करने में पहला कदम इस फ़ाइल को ढूंढना, खोलना और चारों ओर देखना है।
हम जिस लाइब्रेरी का उपयोग कर रहे हैं उसे termios कहा जाता है। OSX Yosemite पर, हेडर फ़ाइल /usr/include/sys/termios.h पर स्थित है। यह लिनक्स पर कहीं और होगी।
यहाँ termios.h कैसा दिखता है। मैंने स्पष्टता के लिए इसे काफी संघनित किया है।
typedef unsigned long tcflag_t; typedef unsigned char cc_t; typedef unsigned long speed_t; struct termios { tcflag_t c_iflag; /* input flags */ tcflag_t c_oflag; /* output flags */ tcflag_t c_cflag; /* control flags */ tcflag_t c_lflag; /* local flags */ cc_t c_cc[NCCS]; /* control chars */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ }; int tcgetattr(int, struct termios *); int tcsetattr(int, int, const struct termios *); int tcflush(int, int);
इस कोड के बारे में तीन महत्वपूर्ण बातें नोटिस करने योग्य हैं। पहला, हमारे पास कुछ typedefs हैं। फिर हमारे पास एक डेटा संरचना है जिसका उपयोग कनेक्शन के लिए कॉन्फ़िगरेशन जानकारी रखने के लिए किया जाता है। अंत में, हमारे पास वे मेथड्स हैं जिनका हम उपयोग करने जा रहे हैं।
Fiddle के इम्पोर्टर का उपयोग करके, हम इन अनुभागों को लगभग शब्दशः अपने Ruby कोड में कॉपी कर सकते हैं। आइए उन्हें एक-एक करके निपटाएं।
टाइप एलियास और टाइपडेफ्स
C में डेटा प्रकारों के लिए एलियास बनाना बहुत आम है। उदाहरण के लिए, termios.h फ़ाइल एक नया प्रकार speed_t बनाती है जो सिर्फ एक लॉन्ग इंटीजर है। Fiddle इम्पोर्टर अपनी टाइपएलियास सुविधा के माध्यम से इन्हें संभालने में सक्षम है।
# `typedef unsigned long tcflag_t;` के समतुल्य typealias "tcflag_t", "unsigned long"
स्ट्रक्ट्स
C में क्लासेस या मॉड्यूल नहीं हैं। यदि आप कई वेरिएबल्स को एक साथ समूहित करना चाहते हैं, तो आप एक स्ट्रक्ट का उपयोग करते हैं। Fiddle Ruby में स्ट्रक्ट्स के साथ काम करने के लिए एक अच्छा तंत्र प्रदान करता है।
पहला कदम Fiddle इम्पोर्टर में स्ट्रक्ट को परिभाषित करना है। जैसा कि आप देख सकते हैं, हम हेडर फ़ाइल से लगभग कॉपी और पेस्ट करने में सक्षम हैं।
Termios = struct [ 'tcflag_t c_iflag', 'tcflag_t c_oflag', 'tcflag_t c_cflag', 'tcflag_t c_lflag', 'cc_t c_cc[20]', 'speed_t c_ispeed', 'speed_t c_ospeed', ]
अब हम malloc मेथड का उपयोग करके स्ट्रक्ट का एक "इंस्टेंस" बना सकते हैं। हम मूल्यों को सेट और प्राप्त कर सकते हैं ठीक उसी तरह जैसे हम किसी सामान्य Ruby क्लास इंस्टेंस के साथ करेंगे।
s = Termios.malloc s.c_iflag = 12345
सब कुछ एक साथ रखना
यदि आप जो हमने अभी टाइपडेफ्स और स्ट्रक्ट्स के बारे में सीखा है उसे जो हमने पहले से ही फ़ंक्शंस के साथ प्रदर्शित किया है, उसके साथ जोड़ते हैं, तो हम termios लाइब्रेरी का एक कार्यशील रैपर एक साथ रख सकते हैं।
हमारा रैपर केवल सीरियल पोर्ट को कॉन्फ़िगर करने के लिए आवश्यक मेथड्स को समाहित करता है। हम बाकी के लिए सादे पुराने Ruby का उपयोग करेंगे।
require 'fiddle' require 'fiddle/import' # इस मॉड्यूल में सब कुछ termios.h से सीधे कॉपी किया गया था। # Yosemite में यह /usr/include/sys/termios.h पर है module Serial extend Fiddle::Importer dlload '/usr/lib/libSystem.dylib' # प्रकार परिभाषाएं typealias "tcflag_t", "unsigned long" typealias "speed_t", "unsigned long" typealias "cc_t", "char" # एक संरचना जो कॉन्फ़िगरेशन डेटा रखेगी। इस तरह इंस्टेंशिएट करें: # `Serial::Termios.malloc()` Termios = struct [ 'tcflag_t c_iflag', 'tcflag_t c_oflag', 'tcflag_t c_cflag', 'tcflag_t c_lflag', 'cc_t c_cc[20]', 'speed_t c_ispeed', 'speed_t c_ospeed', ] # सीरियल डिवाइस के साथ काम करने के लिए फ़ंक्शंस extern 'int tcgetattr(int, struct termios*)' # सीरियल डिवाइस के लिए कॉन्फ़िगरेशन प्राप्त करें extern 'int tcsetattr(int, int, struct termios*)' # सीरियल डिवाइस के लिए कॉन्फ़िगरेशन सेट करें extern 'int tcflush(int, int)' # डिवाइस में सभी बफर्स फ्लश करें end
लाइब्रेरी का उपयोग करना
नीचे दिए गए उदाहरण में हम अपना सीरियल डिवाइस Ruby में खोलते हैं, फिर इसका फ़ाइल डिस्क्रिप्टर प्राप्त करते हैं। हम अपने termios फ़ंक्शंस का उपयोग डिवाइस को पढ़ने के लिए कॉन्फ़िगर करने के लिए करते हैं। फिर हम पढ़ते हैं।
मैं यहां कुछ मैजिक नंबर्स का उपयोग कर रहा हूँ। ये Arduino के साथ संचार के लिए आवश्यक कई बिटवाइज़ कॉन्फ़िगरेशन विकल्पों को संयोजित करने का परिणाम हैं। यदि आप मैजिक नंबर्स कहां से आते हैं में रुचि रखते हैं, तो इस ब्लॉग पोस्ट को देखें।
file = open("/dev/cu.wchusbserial1450", 'r') fd = file.to_i # हमारी कॉन्फ़िगरेशन संरचना का एक नया इंस्टेंस बनाएं config = Serial::Termios.malloc # डिफ़ॉल्ट कॉन्फ़िगरेशन विकल्पों को हमारे स्ट्रक्ट में लोड करें Serial.tcgetattr(fd, config); # Arduino के लिए महत्वपूर्ण कॉन्फ़िगरेशन विकल्प सेट करें। # मैजिक नंबर्स के लिए खेद है। config.c_ispeed = 9600; config.c_ospeed = 9600; config.c_cflag = 51968; config.c_iflag = 0; config.c_oflag = 0; # पढ़ने से पहले 12 कैरेक्टर्स आने का इंतजार करें। config.c_cc[17] = 12; # पढ़ने से पहले प्रतीक्षा करने के लिए कोई न्यूनतम समय नहीं config.c_cc[16] = 0; # हमारा कॉन्फ़िगरेशन सेव करें Serial.tcsetattr(fd, 0, config); # Arduino के रीबूट होने के लिए 1 सेकंड प्रतीक्षा करें sleep(1) # पढ़े जाने की प्रतीक्षा कर रहे किसी भी मौजूदा सीरियल इनपुट को हटा दें Serial.tcflush(fd, 1) buffer = file.read(12) puts "#{ buffer.size } bytes: #{ buffer }" file.close
सीरियल रीडर एक्शन में
यदि मैं अब अपने Arduino को एक प्रोग्राम के साथ लोड करता हूँ जो लगातार हैलो वर्ल्ड प्रिंट करता है, तो मैं अपने Ruby स्क्रिप्ट का उपयोग करके इसे पढ़ सकता हूँ।
यह Arduino प्रोग्राम बस "हैलो वर्ल्ड" को सीरियल पर बार-बार लिखता है
और यहाँ यह दिखता है जब मैं अपना सीरियल मॉनिटर चलाता हूँ।
