फिडल एक अल्पज्ञात मॉड्यूल है जिसे रूबी के मानक पुस्तकालय में 1.9.x में जोड़ा गया था। यह आपको रूबी से सी पुस्तकालयों के साथ सीधे बातचीत करने की अनुमति देता है। यहां तक कि यह आपको रूबी दुभाषिया के चलने पर उसका निरीक्षण और परिवर्तन करने देता है।
यह एक लोकप्रिय सी लाइब्रेरी libffi को लपेटकर काम करता है जो एक भाषा में लिखे गए कोड को दूसरी भाषा में लिखे गए तरीकों को कॉल करने की अनुमति देता है। यदि आपने इसके बारे में नहीं सुना है, तो "ffi" का अर्थ "विदेशी फ़ंक्शन इंटरफ़ेस" है। और आप केवल सी तक ही सीमित नहीं हैं। एक बार जब आप फिडल सीख जाते हैं, तो आप रस्ट और अन्य भाषाओं में लिखी गई पुस्तकालयों का उपयोग कर सकते हैं जो इसका समर्थन करती हैं।
आइए एक नजर डालते हैं फिडल पर। हम एक साधारण उदाहरण के साथ शुरुआत करेंगे और फिर एक सीरियल पोर्ट पर एक Arduino तक पहुंच कर समाप्त करेंगे। आपको इतना जानने की जरूरत नहीं है सी। मैं वादा करता हूँ। :)पी>
एक साधारण उदाहरण
सादे पुराने रूबी में, आपको इसे कॉल करने से पहले हमेशा एक विधि को परिभाषित करना होगा। फिडल के साथ भी ऐसा ही है। आप सीधे सी फ़ंक्शन को कॉल नहीं कर सकते हैं। इसके बजाय आपको सी फ़ंक्शन के लिए एक रैपर बनाना होगा, फिर रैपर को कॉल करना होगा।
नीचे दिए गए उदाहरण में, हम C के लघुगणक फ़ंक्शन को लपेट रहे हैं। हम मूल रूप से रूबी के Math.log
. की नकल कर रहे हैं ।
require 'fiddle'
# We're going to "open" a library, so we have to tell Fiddle where
# it's located on disk. This example works on OSX Yosemite.
libm = Fiddle.dlopen('/usr/lib/libSystem.dylib')
# Create a wrapper for the c function "log".
log = Fiddle::Function.new(
libm['log'], # Get the function from the math library we opened
[Fiddle::TYPE_DOUBLE], # It has one argument, a double, which is similar to ruby's Float
Fiddle::TYPE_DOUBLE # It returns a double
)
# call the c function via our wrapper
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
# We can call the external functions as if they were ruby methods!
puts Logs.log(10) # 2.302585092994046
puts Logs.log10(10) # 1.0
puts Logs.log2(10) # 3.321928094887362
सीरियल पोर्ट को नियंत्रित करना
ठीक है, तो आपने आखिरकार उन Arduinos में से एक खरीद लिया जिसे आप वर्षों से खरीदने के बारे में सोच रहे थे। आपने सीरियल पोर्ट का उपयोग करके प्रति सेकंड एक बार अपने कंप्यूटर पर "हैलो वर्ल्ड" टेक्स्ट भेजा है।
अब, यह वास्तव में अच्छा होगा यदि आप रूबी का उपयोग करके उस डेटा को पढ़ सकें। और आप वास्तव में रूबी के मानक आईओ विधियों का उपयोग करके सीरियल पोर्ट से पढ़ सकते हैं। लेकिन अगर हम हार्डकोर हार्डवेयर हैकर बनना चाहते हैं तो ये IO तरीके हमें सीरियल इनपुट को ग्रैन्युलैरिटी के स्तर के साथ कॉन्फ़िगर करने की अनुमति नहीं देते हैं।
सौभाग्य से, सी मानक पुस्तकालय सीरियल पोर्ट के साथ काम करने के लिए आवश्यक सभी कार्य प्रदान करता है। और फिडल का उपयोग करके, हम रूबी में इन सी कार्यों को एक्सेस कर सकते हैं।
बेशक, पहले से ही ऐसे रत्न हैं जो आपके लिए ऐसा करते हैं। जब मैं इस कोड पर काम कर रहा था तो मैंने रूबीसीरियल मणि से थोड़ी प्रेरणा ली। लेकिन हमारा लक्ष्य यह सीखना है कि यह काम खुद कैसे करें।
परीक्षा termios.h
सी में, .h में समाप्त होने वाला कोई भी फ़ाइल नाम एक हेडर फ़ाइल है। इसमें उन सभी कार्यों और स्थिरांकों की एक सूची है जो पुस्तकालय तृतीय पक्ष कोड (हमारा कोड) को उपलब्ध कराता है। किसी भी सी लाइब्रेरी को लपेटने में पहला कदम इस फाइल को ढूंढना, इसे खोलना और चारों ओर देखना है।
हम जिस पुस्तकालय का उपयोग कर रहे हैं उसे टर्मियोस कहा जाता है। OSX Yosemite पर, हेडर फ़ाइल /usr/include/sys/termios.h पर स्थित होती है। यह linux पर कहीं और होगा।
यहाँ क्या 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 लिखें
सी में डेटा प्रकारों के लिए उपनाम बनाना बहुत आम है। उदाहरण के लिए, termios.h फ़ाइल एक नया प्रकार बनाती है जिसे speed_t कहा जाता है जो कि केवल एक लंबा पूर्णांक है। फिडल आयातक अपनी टाइपियास सुविधा के माध्यम से इन्हें संभालने में सक्षम है।
# equivalent to `typedef unsigned long tcflag_t;`
typealias "tcflag_t", "unsigned long"
संरचनाएं
सी में कक्षाएं या मॉड्यूल नहीं हैं। यदि आप चर के एक समूह को एक साथ समूहित करना चाहते हैं, तो आप एक संरचना का उपयोग करते हैं। रूबी में स्ट्रक्चर्स के साथ काम करने के लिए फिडल एक अच्छा मैकेनिज्म प्रदान करता है।
पहला कदम फिडल आयातक में संरचना को परिभाषित करना है। जैसा कि आप देख सकते हैं, हम हेडर फ़ाइल से कॉपी और पेस्ट करने में लगभग सक्षम हैं।
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',
]
अब हम मॉलोक विधि का उपयोग करके संरचना का "उदाहरण" बना सकते हैं। हम मानों को उसी तरह सेट और पुनर्प्राप्त कर सकते हैं जैसे हम किसी भी सामान्य रूबी क्लास इंस्टेंस के साथ करते हैं।
s = Termios.malloc
s.c_iflag = 12345
सब को एक साथ रखना
यदि आप टाइपिफ़ और स्ट्रक्चर्स के बारे में सीखी गई चीज़ों को फंक्शन्स के साथ पहले से प्रदर्शित किए गए कार्यों के साथ जोड़ते हैं, तो हम टर्मियोस लाइब्रेरी के वर्किंग रैपर को एक साथ रख सकते हैं।
हमारे रैपर में केवल वे तरीके हैं जिनकी हमें सीरियल पोर्ट को कॉन्फ़िगर करने की आवश्यकता है। हम बाकी के लिए सादे पुराने माणिक का उपयोग करेंगे।
require 'fiddle'
require 'fiddle/import'
# Everything in this module was pretty much copied directly from
# termios.h. In Yosemite it's at /usr/include/sys/termios.h
module Serial
extend Fiddle::Importer
dlload '/usr/lib/libSystem.dylib'
# Type definitions
typealias "tcflag_t", "unsigned long"
typealias "speed_t", "unsigned long"
typealias "cc_t", "char"
# A structure which will hold configuratin data. Instantiate like
# so: `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',
]
# Functions for working with a serial device
extern 'int tcgetattr(int, struct termios*)' # get the config for a serial device
extern 'int tcsetattr(int, int, struct termios*)' # set the config for a serial device
extern 'int tcflush(int, int)' # flush all buffers in the device
end
लाइब्रेरी का उपयोग करना
नीचे दिए गए उदाहरण में हम अपने सीरियल डिवाइस को रूबी में खोलते हैं, फिर उसका फाइल डिस्क्रिप्टर प्राप्त करते हैं। हम पढ़ने के लिए डिवाइस को कॉन्फ़िगर करने के लिए अपने टर्मियो फ़ंक्शन का उपयोग करते हैं। फिर हम पढ़ते हैं।
मैं यहाँ कुछ जादुई संख्याओं का उपयोग कर रहा हूँ। ये बिटवाइज़ कॉन्फ़िगरेशन विकल्पों के एक समूह के संयोजन का परिणाम हैं जो कि arduino के साथ संचार के लिए आवश्यक हैं। यदि आप रुचि रखते हैं कि जादुई संख्याएँ कहाँ से आती हैं, तो इस ब्लॉग पोस्ट को देखें।
file = open("/dev/cu.wchusbserial1450", 'r')
fd = file.to_i
# Create a new instance of our config structure
config = Serial::Termios.malloc
# Load the default config options into our struct
Serial.tcgetattr(fd, config);
# Set config options important to the arduino.
# I'm sorry for the magic numbers.
config.c_ispeed = 9600;
config.c_ospeed = 9600;
config.c_cflag = 51968;
config.c_iflag = 0;
config.c_oflag = 0;
# wait for 12 characters to come in before read returns.
config.c_cc[17] = 12;
# no minimum time to wait before read returns
config.c_cc[16] = 0;
# Save our configuration
Serial.tcsetattr(fd, 0, config);
# Wait 1 second for the arduino to reboot
sleep(1)
# Remove any existing serial input waiting to be read
Serial.tcflush(fd, 1)
buffer = file.read(12)
puts "#{ buffer.size } bytes: #{ buffer }"
file.close
सीरियल रीडर एक्शन में है
अगर मैं अब अपने arduino को एक प्रोग्राम के साथ लोड करता हूं जो लगातार हैलो वर्ल्ड प्रिंट करता है, तो मैं इसे पढ़ने के लिए अपनी रूबी स्क्रिप्ट का उपयोग कर सकता हूं।
यह arduino प्रोग्राम सीरियल को बार-बार "हैलो वर्ल्ड" लिखता है
और जब मैं अपना सीरियल मॉनीटर चलाता हूं तो यह कैसा दिखता है।