გამარჯობათ, ეს არის ჩემს მიერ ბოლო დროს გაკეთებული პროექტი, რომელიც საკმაოდ სახალისო და ამავე დროს საინტერესოა ჩემთვის, რომელიც იყენებს ახალ AtMega32 ჩიპს, რომელსაც აქვს USB კომუნიკაციის მხარდაჭერა პირდაპირ კომპიუტერთან რაიმე FTDI ჩიპის გამოყენების გარეშე, რაც აქამდე ასე თუ ისე მოყვარულთათვის დიდი თავის სატკივარი იყო.
პროექტის განვითარების ფარგლებში იდეების მოწოდება შეგიძლიათ ქვემოთ - კომენტარებში, ან მეილის გამოყენებით - Giorgi.jambazishvilI@gmail.com
ამ ვერსიაში, რომელიც პირველია და ყველაზე ახალი, იმპლემენტირებულია თაგვის (მაგრამ მე მოვიხსენიებ როგორ მაუსს) თითქმის სრული ვარიანტი, თუ არ ჩათვლით შუა ღილაკს, რომელიც საჭიროა კონტექსტში ნავიგაციისთვის, მაგრამ ეს არ არის პრობლემა, რადგან უფრო დიდი გეგმები მაქვს განსახორციელებელი ამ მოწყობილობასთან დაკავშირებით.
დავიწყებ აღწერით, რომელიც მთლიანად მოიცავს იმ ასორტიმენტს, რომელიც ძირითად როლს თამაშობს ამ მოწყობილობაში.
1) ხელთათმანი, რომელზეც მოწყობილია ეს ყველაფერი. უფრო კარგი იქნება თუ მთლიან ხელს არ დაიკავებს და უბრალოდ ადვილად
დასამაგრებელ/მოსაშორებელი რამ იქნება მაგრამ მომავლისთვის
2) იზოლაციის ლენტა, რომელიც ამ პროექტზე "ოხრად" არის გამოყენებული, ვიზუალური მხარის გაუთვალისწინებლად
3) ფოლგა, რომელიც ნებისმიერ მაღაზიაში უნდა იყიდებოდეს, საკმაოდ კარგი რაღაცაა და ბევრი რამის გაკეთებაა შესაძლებელი. ჩემს შემთხვევაში კი ეს ერთგვარ ღილაკად არის გაკეთებული
4) პროცესორი, რომელიც უფრო კომფორტულს ხდის მაუსის მოძრაობის პროცესს. ამას ყოველთვის იმიტომ ავღნიშნავ, რომ როდესაც სულ თავიდან აქსელერომეტრიდან აღებული სიგნალები გადავგზავნე კომპიუტერში მაუსის გასამოძრავებლად, რაღაც კორიდის ხარის მსგავსი დაუმორჩილებელი კურსორი მივიღე. ამის შემდეგ დავიწყე სიგნალის დამუშავება და ჟესტიკულაციის ამოცნობის ალგორითმის დამატება. ასევე პროცესორის "წყალობითაა", რომ კომპიუტერს ეს ხელსაწყო მაუსი ჰქონია
5) კარგი უნარი და ბაზა C/C++, მაგრამ მაინც უფრო C ში, რადგან C++ იდან კლასებს არ ვიყენებ და შეუძლებელიცაა ესეთ პატარა პროცესორში ამ ყველაფრის ჩადება
მუშაობის მთავარი პრინციპი ის არის, რომ თუ დავიწყებთ ხელიდან და გავუყვებით USB შესაერთებლამდე ვნახავთ, რომ ყველაფერი საკმაოდ ჭკვიანურად არის მოწყობილი. ხელიდან გამოდის 5 ხაზი, რომელიდანაც 2 ემსახურება VCC სა და GND (+3.3V და 0V). კიდევ ორი - SDA და SCL (TWI ინტერფეისი) და ერთიც მაუსის ღილაკებისათვის. თავიდან შეიძლება უცნაურად მოგეჩვენოთ როგორ ხდება ორი ღილაკის ინფორმაციის გადაცემა ერთი ხაზით, მაგრამ უფრო უცნაურად ის მოგეჩვენებათ, რომ გითხრათ, რომ ამ ერთი ხაზის გამოყენებით შესაძლებელია ხელზე 50 დან 200 მდე ღილაკი მოვითავსო ხელზე და ისე გამოვიყენო ისინი, როგორც მომინდება. ეს ყველაფერი კი უბრალო ანალოგური პინის (მიკრო პროცესორზე), პროგრამისა და წინაღობების დამსახურებაა.
როდესაც ამ ხუთი ხაზის გავლით ინფორმაცია გადაიგზავნება მიკროპროცესორში, ამის შემდეგ იწყება ყველაზე მთავარი - მათი დამუშავება, რაც საკმაოდ "სატანჯველია", მაგრამ შედეგზე ფიქრი მეხმარებოდა. როდესაც ყველაფერი კარგად "გაიწმინდება" და ინფორმაცია გადამუშავდება ისე, რომ მზად იყოს კომპიუტერში გადასაგზავნად, ამის შემდეგ უკვე კომპიუტერის მოტყუებას აქვს ადგილი, როცა პროცესორი მას მაუსი ჰქონია, მაგრამ ეს ასეც არის.
ბოლოს კი ვიდეო
დროის გამოჩენის პარალელურად გავაგრძელებ განახლებებზე და დახვეწაზე მუშაობას, რასაც ამ ბლოგზე დავდებ...
გიორგი ჯამბაზიშვილი (G.J)
Sunday, February 9, 2014
Friday, January 31, 2014
I2C კომუნიკაციის პროტოკოლი
I კვადრატ C, არის მრავალ მაკონტროლებიანი სერიული დისტრიბუტორი. გამოგონებულ იქნა Phillips Semiconductor დანაყოფის მიერ. მისი გამოყენება ძირითადად ხდება დედა დაფებისთვის დაბალი სიხშირის პერიფერიების მიერთებისთვის. ასევე გამოიყენება ჩაშენებულ სისტემებში, მობილურ ტელეფონებში და სხვა ციფრულ მოწყობილობებში.
ტექნოლოგიების განვითარებასთან ერთად 1982 წლიდან 2012 წლამდე კომუნიკაციის სიჩქარე 100 კილო ჰერციდან გაიზარდა 5 მეგა ჰერცამდე (შედარებისთვის 5000 კილო ჰერცი). საბოლოო ვერსია არის 5, რომელშიც გამოსწორებული წინა ვერსიის შეცდომები.
მთავარი აზრი ის არის, რომ I2C იყენებს ორ უნივერსალურ (ორმაგი მიმართულების) ხაზს. Serial Data და Serial Clock, შესაბამისად - SDA და SCL ხაზს, რომელიც აწეულია წინაღობებით. გააჩნია 7 / 10 ბიტიანი მისამართის სივრცე, რომელიც გამოყენებულ მოწყობილობებზეა დამოკიდებული. სტანდარტული ინფორმაციის გაცვლის მდგომარეობა არის 100kbit/s.
კომუნიკაციის მხრივ არსებობს 4 გარემოება, რომელიც შესაძლებელია წარმოიშვას:
1. მაკონტროლებელი (master) კვანძი უგზავნის მონაცემს დაქვემდებარებულს (slave)
2. მაკონტროლებელი იღებს ინფორმაციას დაქვემდებარებულიაგან
3. დაქვემდებარებული კვანძი უგზავნის მაკონტროლებელს ინფორმაციას
4. დაქვემდებარებული კვანძი იღებს ინფორმაციას მაკონტროლებლისგან
როგორც აღვნიშნე I2C კომუნიკაცია საკმაოდ მარტივია და ძალუძს რამდენიმე მოწყობილობას შორის გაცვალოს ინფორმაცია მხოლოდ 2 ინფორმაციის მიმოცვლის ხაზის გამოყენებით. ყველაზე მარტივ ფორმაში არსებობს მაკონტროლებელი მოწყობილობა, რომელსაც შეუძლია აკონტროლოს დაქვემდებარებული რამდენიმე მოწყობილობა. აღსანიშნავია ის ფაქტი, რომ SCL, ანუ რომ იმპულსების ხაზს აკონტროლებს მაკონტროლებელი მოწყობილობა. კომუნიკაციის დასამყარებლად მაკონტროლებელი მოწყობილობა აგზავნის 1 ბაიტ ინფორმაციას, რომელშიც შერეულია დაქვემდებარებული მოწყობილობის მისამართი (ID) და ჩაწერა/წაკითხვის 1 ბიტიანი მანიშნებელი. თუ განსაზღვრულია ჩაწერის ოპერაცია, მაშინ მაკონტროლებელი მოწყობილობა დაიწყებს ინფორმაციის გაგზავნას იმავე მისამართზე. ხოლო თუ 1 ბიტი მიუთითებს იმაზე, რომ წაკითხვა უნდა შესრულდეს, მაშინ დაქვემდებარებული მოწყობილობა გაგზავნის ინფორმაციას, რომელსაც წაიკითხავს მთავარი მოწყობილობა (მაკონტროლებელი). ტრანზაქციების გასაკონტროლებლად მთავარი და დაქვემდებარებული მოწყობილობები აკონტროლებენ SCL და SDA ხაზებს, რომელმაც შეიძლება შემდეგი მნიშვნელობები მიიღოს: START, STOP, ACK, NAK. ბოლო ორი შესაბამისად acknowledged და no acknowledge. მდგომარეობის დეტალები უკვე იმართება სპეციალური დრაივერების დახმარებით.
R/W - წაკითხვა/ჩაწერა, Slave Address - დაქვემდებარებულის მისამართი.
ელექტრონულად გამართულობის მხრივ, მოთხოვნები საკმაოდ მარტივია. მთავარმა და დაქვემდებარებულმა უნდა გამოიყენონ ერთი და იმავე დონის ძაბვა (შენიშვნა 1). Pull-up წინაღობების მნიშვნელობა დაახლოებით გამოითვლება კავშირის სრულ ტევადობაზე დაყრდნობით, მაგრამ 1.8K დან 10K მდე, პრაქტიკულად, შესაძლებელია ნებისმიერი წინაღობის გამოყენება (შენიშვნა 2).
სხვადასხვა მიკრო პროცესორი ამ კომუნიკაციის იმპლემენტაციას სხვადასხვაგვარად ახორციელებს. ზოგიერთმა შეიძლება გამოიყენოს USI (Universal Serial Interface), რომელიც ასევე გამოდგება SPI კომუნიკაციებისათვის, ხოლო ზოგიერთს გააჩნია სპეციალურად ჩაშენებული TWI (Two Wire Interface) კერძოდ I2C კომუნიკაციებისათვის.
როგორც აღვნიშნე I2C მოწყობილობების მისამართი არის 7 ბიტიანი +1 ბიტი ჩაწერა/წაკითხვა. ამიტომ აქედან გამომდინარე 127 მოწყობილობის დაკავშირებაა შესაძლებელი მხოლოდ ორი ხაზის გამოყენებით. როგორც ზემოთ არის ნაჩვენები (მისამართის სურათი) 7 ბიტი წანაცვლებულია 1 ბიტით და ბოლო პირველი ბიტი გამოყენებულია, როგორც ჩაწერა/წაკითხვის მანიშნებელი. ზოგჯერ არის ისეთი შემთხვევები, როდესაც საჭირო ხდება დაქვემდებარებული მოწყობილობის მისამართის განსაზღვრა ფიზიკურად - დაფაზე. ხშირად ბოლოდან 4 ბიტი განსაზღვრულია წინასწარ და მომხმარებელს უწევს 3 ბიტის ფიზიკურად კონფიგურაცია. მსგავსი რამ შეიძლება შეგხვდეთ გარე მახსოვრობის მიკრო სქემების სარგებლობისას.
არსებობს ასევე ისეთი გამონაკლისი შემთხვევები, როდესაც 1 მოწყობილობა იყენებს რამდენიმე მისამართს. ასეთი სიტუაცია შეიძლება შეგექმნათ EEPROM ებთან მუშაობისას, რომლებიც გარე მახსოვრობას წარმოადგენენ. დავუშვათ, რომ ავიღოთ, 24C16 ჩიპი, რომლის მახსოვრობა 16K ბაიტია, ამ დროს მონაცემთა ოდენობის გაზრდის პარალელურად (როგორც აღვნიშნეთ მისამართის ბოლოდან პირველი 4 ბიტი განსაზღვრულია, კერძოდ - 1010) აღარ არის საჭირო 2-4 (ჩათვლით) ბიტების ფიზიკური კონფიგურაცია, რადგან აზრი არ აქვს. ამიტომ მთელი მოწყობილობა იყენებს 50-57 მისამართს, რომ მთავარმა მოწყობილობამ შეძლოს მთელი 16K ბაიტი ინფორმაციის აღქმა და შესაბამისად მისი მანიპულირება მოახდინოს.
1. ზოგჯერ დაქვემდებარებულ მოწყობილობას აქვს შეზღუდვა ძაბვაზე, მაგალითად მუშაობს მხოლოდ 2.5 – 3.3 ვოლტამდე. ეს შეზღუდვა უნდა შესრულდეს, რომ არ გაფუჭდეს მოწყობილობა. ამასთან ერთად უნდა გაითვალისწინოთ ისიც, რომ მთავარმა მოწყობილობამაც უნდა იმუშაოს ამ დიაპაზონში.
2. სრული ტევადობის პრობლემა მანამდე არ იჩენს თავს, სანამ არ იქნება გრძელი ხაზები და დაქვემდებარებული მოწყობილობების გრძელი ჩამონათვალი.
პრაქტიკული გამოყენება
ამდენი თეორიის შემდეგ საინტერესო იქნება თუ როგორ შეგვიძლია ეს ყველაფერი გადავიტანოთ რეალურ ცხოვრებაში და ვნახოთ ამ კომუნიკაციის საშუალების რეალური შედეგები. მაგალითში გამოვიყენებ დღესდღეობით საკმაოდ პოპულარულ დაფას - Arduino და მის IDE ს. აქსელერომეტრის ვარიანტში კი გამოვიყენებ უბრალოდ Atmega32 ჩიპს, რომელსაც USB კომუნიკაციის მხარდაჭერა აქვს.
მე პირადად კომუნიკაციის ეს ტიპი გამომიყენებია გარე მახსოვრობებისათვის, ტემპერატურისა და ტენიანობის გაზომვისათვის და აქსელერომეტრის მონაცემების წასაკითხად. აქსელერომეტრთან და გარე მახსოვრობასთან მუშაობა ერთის მხრივ უფრო რთული იმ მხრივ არის, რომ ორმხრივი კომუნიკაციაა საჭირო, მონაცემების ჩასაწერად და შემდეგ წასაკითხად (სასურველ ფორმატში აქსელერომეტრის შემთხვევაში). პირველი მაგალითი დავიწყოთ გარე მახსოვრობასთან კომუნიკაციით, რომელიც ამ მაგალითის შემთხვევაში არის 24LC256 (256K ბიტიანი = 32K ბაიტი გარე მახსოვრობა).
A1 A2 და A3 პინების მდგომარეობის მიხედვით შესაძლებელია მისამართის ამ მოწყობილობის მისამართის განსაზღვრა. მაგალითისთვის ავიღოთ, თუ სამივე ფეხს მივაერთებთ ნულთან, მაშინ მოწყობილობის მისამართი გახდება 0x50, ხოლო თუ ყველა ფეხს + ზე მივაერთებთ მისამართი შეიცვლება 0x57 ით. ყველა კომბინაციისას მისამართი იცვლება, რომლის მნიშვნელობა შესაძლებელია მონაცემთა ფურცელის ან google ის დახმარებით გაიგოთ. სიმარტივისთვის ყველა ფეხს შევაერთებ ნულთან.
Arduono სთვის კოდი კი ქვემოთ იქნება მოცემული. პირველ რიგში setup ში საჭიროა ინტერფეისის დაწყება, მხოლოდ იმის შემდეგ, რაც გლობალურ ბლოკში შემოვიტანთ სპეციალურად ამ ინტერფეისისთვის დაწერილ ბიბლიოთეკას - Wire.h. საბოლოოდ კოდი გამოდის შემდეგნაირი, რომელსაც დაწვრილებით გავარჩევ (კოდი წარმოდგენილია, როგორც გრაფიკული ფაილი, მისი რედაქტირება შესაძლებელია სტატიის ბოლოს არსებული ფაილის საშუალებით Arduino IDE ს გამოყენებით).
თავიდან შეიძლება writeEEPROM და readEEPROM ფუნქციები ძნელი აღსაქმელი გახდეს, მაგრამ ახსნილი ვფიქრობ გაცილებით მარტივი იქნება. კოდში მაქვს სტანდარტული loop და setup ფუნქციები (მეთოდი - C# ში). ამჯერად მეორე ფუნქცია ცარიელია, რადგან მანიპულირება სურვილისებრ შეგიძლიათ მოახდინოთ. პირველად setup ფუნქციაში ვახდენ Serial პორტის ინიციალიზებას, რომლის პარამეტრი კომუნიკაციის სიჩქარეა (სერიული პორტი კომპიუტერთან კომუნიკაციის საშუალებაა Arduino სთვის). სერიული მონიტორისა და პროცესორის კომუნიკაციის სიჩქარე უნდა დაემთხვეს, რომ კომუნიკაცია წარმატებით შედგეს, მონაცემების გამოსატანად.
ამის შემდეგ ხდება I2C კომუნიკაციის დაწყება, რაც (სტანდარტულად) მიკროპროცესორის ანალოგიურ მეხუთე და მეოთხე პინს გამოიყენებს კომუნიკაციისთვის. ამის შემდეგ იქმნება ცვლადი, მისმართი, რომელიც მიუთითებს ადგილს მახსოვრობაში ინფორმაციის ჩაწერის მისამართს. ყოველ ჯერზე უნდა გაიგზავნოს 1 ბაიტი. როგორც აღვნიშნეთ ჩემს შემთხვევაში მოწყობილობის მოცულობაა 32K ბაიტი. თუ მისამართს მივუთითებ ნულს, მაშინ ინფორმაციის ჩაწერა დაიწყება ნულიდან, ხოლო თუ 321, 321 დან. ასევე მაქვს ორი ძირითადი ფუნქცია writeEEPROM და readEEPROM, რომლებიც მთელ შავ სამუშაოს ასრულებენ.
გარჩევა დავიწყოთ ჩაწერის ფუნქციით, რომელიც სამ არგუმენტს იღებს: მოწყობილობის მისამართს, მეხსიერებაში ჩაწერის დაწყების მისამართსა და 1 ბაიტ ინფორმაციას, რომელიც უნდა გაიგზავნოს გარე მახსოვრობაში TWI ის გამოყენებით. Setup ფუნქციაში ვიძახებ ჩაწერის ფუნქციას და ვუთითებ მასში, რომ ჩემს გარე მოწყობილობაში ჩაწეროს 123. ახლა კი დროა უფრო ღრმად ვნახოთ რას შვრება ჩემი კოდი რელურად და როგორ უმკლავდება მონაცემთა გაანალიზებას.
ინფორმაციისთვის : Arduino IDE ში int ცვლადი იკავებს 2 ბაიტს, ხოლო char ცვლადი 1 ბაიტს. I2C კომუნიკაციის გამოყენებისას შეზღუდვა ის არის, რომ ყოველი გაგზავნილი პაკეტი უნდა იყოს 1 ბაიტის ტოლი.
პირველ რიგში, ჩაწერის ფუნქცია იწყება Wire.beginTransmission() ით, რაც მოწყობილობას ამცნობს იმას, რომ კომუნიკაციის დამყარება გვსურს მასთან. შემდეგ ვგზავნით მეხსიერების იმ მისამართს, სადაც ჩაწერის განხორციელება გვსურს. რადგან გარე მეხსიერებაში 32000 სხვადასხვა მისამართი არსებობს, რისთვისაც 16 ბიტიანი ცვლადის გამოყენებაა საჭირო, უნდა მოვახერხო და ეს 16 ბიტი, ორ 8 ბიტიან ცვლადად დავშალო და მისამართი ისე გავაგზავნო. მეორე ხაზზე, ხდება 8 ბიტის გადატანა მარჯვნივ და ამ 8 ბიტის გაგზავნა. ამის შემდეგ დარჩენილი 8 ბიტის გაგზავნა. განვიხილავ მაგალითს:
ინფორმაციისთვის : MSB – Most Significant Bit. LSB – Less Significant Bit. MSB ით აღინიშნება პირველი 8 ბიტი (მარცხნიდან), ხოლო LSB ით პირველი 8 ბიტი (მარჯვნიდან).
საჭიროა გავგზავნოთ მისამართი 20 000, რომელიც ორობით სისტემაში არის 0100 1110 0010 0000. ჯერ უნდა გავგზავნოთ MSB და შემდეგ LSB. 16 ბიტიდან ჯერ უნდა მივიღოთ MSB ის ბიტები, ამიტომ ხდება მათი 8 პოზიციით გადატანა.
0100 1110 0010 0000 -> 8 ბიტის გადაწევისას -> 0100 1110
მესამე ხაზზე კი სრულდება შემდეგი ინსტრუქცია (დაპროგრამებაში ეგრეთწოდებული შენიღბვა (masking)) ლოგიკური ‘და’ ს გამოყენებით.
0100 1110 0010 0000 -> შენიღბვა -> 0010 0000
ეს იმას ნიშნავს, რომ 24LC256 ჩიპი მონაცემებს იღებს შემდეგნაირად, ჯერ 0100 1110 და შემდეგ 0010 0000, რომლითაც ის ხვდება, რომ ინფორმაციის მოთავსება იწყება 20 000 დან. ამით ჩვენ მას ვამცნობთ, რომ გვინდა მასში ინფორმაციის ჩაწერა გადაცემულ მისამართზე. და ვაგზავნი კიდევაც ინფორმაციას. ამის შემდეგ უკვე ხდება კომუნიკაციის დასრულება იმით, რომ ჩიპი თავის თავში წერს მიღებულ ინფორმაციას. ასევე არ უნდა დაგავიწყდეთ 5 მილი წამის ოდენობის შეყოვნება, რადგან მოწყობილობამ მოასწროს ინფორმაციის შენახვა. როდესაც არის მომენტები, რომლებშიც მიმდევრობით ხდება ინფორმაციის ჩაწერა, ამ დროს შეყოვნების გარეშე შეიძლება ყველაფერი ცუდად წავიდეს და უცნაური რამე მოხდეს.
ამის შემდეგ მეორე, წაკითხვის ფუნქცია, მისამართის კონვერტირებას 2 ბაიტიდან იგივე გზით ახდენს 2 ცალ 1 ბაიტში. უგზავნის მოწყობილობას ამ მისამართს და ასრულებს კომუნიკაციას. შემდეგ ითხოვს მოწყობილობისგან ინფორმაციას. Wire.requestFrom() ფუნქციის მეორე პარამეტრი აღნიშნავს თუ რამდენი ბაიტის წაკითხვა არის საჭირო (ჩვენს შემთხვევაში 1 ის). შემდეგ ვამოწმებ არის თუ არა ინფორმაცია I2C ს ხაზზე და ვკითხულობ მას. ამის შემდეგ ფუნქცია აბრუნებს 1 ბაიტ ინფორმაციას.
ვფიქრობ, ამით ამოვწურე I2C კომუნიკაციის პროტოკოლის გამოყენება AVR ის მიკრო პროცესორებში საწყისი ეტაპისათვის. მომავალში უფრო მეტ მაგალითს განვიხილავ, როგორც კი მექნება საშუალება. ამ ლინკზე შეგიძლიათ ნახოთ პროგრამის კოდი და გამოიყენოთ სურვილისებრ.
Friday, January 10, 2014
მოვლენები (2/3) - მოვლენის მსმენელი
ამ პოსტში მინდა ვისაუბრო იმ მოვლენისა და კლასების გამოყენებაზე, რომელიც განვსაზღვრე წინა პოსტში და მოვახდინო მათი ინტეგრირება მხოლოდ ერთი მარრტივი კლასის დახმარებით, რაც მთლიანობაში აზრობრივად დაასრულებს მოვლენებს, ხოლო მესამე ნაწილი დაეთმობა სუსტ მოვლენებს (Weak Events).
კლასი User.cs გამოიყენება, როგორც მოვლენის მსმენელი. ეს კლასი იწერს JobDealer.cs ის მოვლენას და განსაზღვრავს ახალ მეთოდს, NewJobIsAvaiilable, რომელიც აკმაყოფილებს EventHandler<JobInfoEventArgs> ტიპის დელეგატის ყველა მოთხოვნას, კერძოდ, აქვს ორი პარამეტრი: ობიექტი და JobInfoEventArgs
public class User
{
private stringname;
publicUser(string Name)
{
this.name = Name;
}
public voidNewJobIsAvailable(object sender, JobInfoEventArgse)
{
Console.WriteLine("{0}: job {1} is new and available", name, e.Job);
}
}
ახლა მოვლენის გამომქვეყნებელი და მისი გამომწერი უნდა დაუკავშირდნენ ერთმანეთს რამენაირად. ამის გაკეთება კი შესაძლებელია NewJobInfo მოვლენის გამოყენებით, რომელიც განსაზღვრულია JobDealer.cs ში. გამოვიყენებ += ოპერატორს გამოწერის შესასრულებლად. მოცემულია სანიმუშო კოდი, ქვემოთ
static voidMain(string[] args)
{
vardealer = new JobDealer();
varMisha = new User("MISHA");
dealer.NewJobInfo += Misha.NewJobIsAvailable;
dealer.NewJob("Manager");
varGiorgi = new User("GIORGI");
dealer.NewJobInfo += Giorgi.NewJobIsAvailable;
dealer.NewJob("Developer");
Console.ReadKey();
}
პროგრამის გაშვების შემთხვევაში გვექნება
ყოველი ახალი სამსახურის დამატებისას, ის მომხმარებელი, რომელსაც გამოწერილი აქვს მოვლენა, მისდის შეტყობინება ახალი სამსახურის შესახებ, როდესაც ხდება მოვლენის გააქტიურება.
Monday, January 6, 2014
მოვლენები (1/3) - მოვლენის განმსაზღვრელი
მოვლენები მთლიანად დაფუძნებულები არიან დელეგატებზე და რახან მოვრჩი დელეგატების განხილვას, ლოგიკური იქნება თუ ჩემი პოსტების გაგრძელება შეეხება ისეთ ელემენტებს, როგორიცაა მოვლენები. მოვლენები გვთავაზობს publish/subscribe მექანიზმს. ყველაფრის გადათარგმნა არ იქნება მოხერხებული და ამიტომ, ჩემი აზრით, სჯობს რამდენიმე ტერმინოლოგია ასე დავტოვო. მაგალითისათვის, რომ ავიღოთ Windows Forms ში Button კონტროლს აქვს Click მოვლენა. მოვლენის ეს ტიპი არის დელეგატი, ხოლო მომგვარებელი (handler) მეთოდის გამოძახება ხდება მაშინ, როდესაც აქტიურდება Click მოვლენა. მომგვარებელი ანუ მოპასუხე მეთოდის განსაზღვრა ხდება მომხმარებლის მიერ უმეტეს შემთხვევაში.
ჩემი აზრით მხოლოდ თეორიას ჯობს მაგალითებიც შევურიო მასში და ასეც მოვიქცევი. შევქმნი ახალ კლასს - JobDealer.cs, რომელიც სთავაზობს „მომხმარებელს“ მოვლენებზე დაფუძნებულ „გამოწერას“. JobDealer.cs განსაზღვრავს ახალ მოვლენას NewJobInfo, რომლის ტიპია EventHandler<JobInfoEventArgs> event საკვანძო სიტყვის გამოყენებით. NewJob() მეთოდის შიგნით ხდება NewJobInfo მოვლენის გააქტიურება.
public class JobInfoEventArgs: EventArgs
{
publicJobInfoEventArgs(string job)
{
this.Job = job;
}
public stringJob { get; private set; }
}
public class JobDealer
{
public event EventHandler<JobInfoEventArgs> NewJobInfo;
public voidNewJob(string job)
{
Console.WriteLine("JobDealer, new job {0}", job);
if(NewJobInfo != null)
{
NewJobInfo(this, new JobInfoEventArgs(job));
}
}
}
JobDealer.cs კლასი მომხმარებელს სთავაზობს ახალ მოვლენას NewJobInfo რომლის ტიპია <JobInfoEventArgs>. კონვენციის თანახმად მოვლენები იყენებენ მეთოდებს, რომლებსაც გააჩნიათ ორი პარამეტრი. Sender ობიექტი, რომელიც ააქტიურებს მოვლენას და მეორე პარამეტრი აგზავნის მოვლენის შესახებ დამატებით ინფორმაციას. მეორე პარამეტრი სხვადასხვაა სხვადასხვა ტიპის მოვლენებისათვის. .NET 1.0 ში უკვე რამდენიმე ასეული დელეგატი იყო განსაზღვრული სხვადასხვა ტიპის მოვლენებისათვის. EventHandler<TEventArgs> განსაზღვრავს handlerს რომელიც აბრუნებს void ს და იღებს ორ არგუმენტს. EventHandler<TEventArgs> ის პირველი პარამეტრი უნდა იყოს object ტიპის, ხოლო მეორე პარამეტრის ტიპი არის T. EventHandler<TEventArgs> ასევე განსაზღვრავს T ტიპზე შეზღუდვას, რაც იმას ნიშნავს, რომ მიუხედავად იმისა, რომ T ტიპში ნებისმიერი ტიპი შეიძლება იგულისხმებოდეს, მაინც არსებობს გამონაკლისები და ყველა ტიპი არ შეიძლება ჩაჯდეს <> ფრჩხილებში. ეს შეზღუდვა კი მდებარეობს შემდეგში: T ს მაგივრად გამოყენებული კლასი აუცილებელია, რომ იყოს EventArgs ის მემკვიდრე. სწორედ ამიტომ არის გამოყენებული ჩემს მიერ შემოთავაზებულ მაგალითში განსაზღვრული კლასისთვის წინაპარი EventArgs კლასი.
public delegate void EventHandler<TEventArgs> (object sender, TEventArgs e)
whereTEventArgs : EventArgs
კლასი JobDealer.cs ააქტიურებს მოვლენას NewJob მეთოდში. NewJobInfo ს () ით გამოყენებით, იძახებთ ყველა იმ handler ს რომელსაც „გამოწერილი“ აქვს მოვლენა. Multicast დელეგატების მსგავსად handler ების გამოძახების თანმიმდევრობა არ არის დაცული, ამიტომ ესეც უნდა გაითვალისწინოთ თქვენს მიერ დაწერილ კოდში, რადგან ერთხელ შეიძლება იმუშაოს ყველაფერმა, ხოლო მეორეჯერ კი რაიმე შეცდომა ამოაგდოს და თავის ტკივილი გაგიჩინოთ.
ასევე ყურადღებაა იმ ფაქტზე გასამახვილებელი, რომ აუცილებლად უნდა შემოწმდეს NewJobInfo დელეგატის არსებობა, რადგან თუ ის არავის არ აქვს გამოწერილი მაშინ null ის ტოლია.
public voidNewJob(string job)
{
Console.WriteLine("JobDealer, new job {0}", job);
if(NewJobInfo != null)
{
NewJobInfo(this, new JobInfoEventArgs(job));
}
}
Saturday, January 4, 2014
დელეგატები (5/5) - Multicast Delegates
აქამდე ვიყენებდი დელეგატებს, რომლებიც იძახებდნენ მხოლოდ ერთ მეთოდს. ერთზე მეტი მეთოდის გამოსაძახებელად ერთი დელეგატის გამოყენებით საჭიროა ცხადად შეუცვალოთ დელეგატს შემცველი მეთოდის მისამართი. მაგრამ არსებობს უფრო მარტივი გზაც, რომელიც განსხვავდება პირველი პრიმიტიული გზისაგან და საშუალებას გვაძლევს ერთი დელეგატის გამოყენებით გამოვიძახოთ ერთზე მეტი მეთოდი. მსგავსი დელეგატი ცნობილია Multicast დელეგატის სახელით. თავისუფლად შეიძლება მას ეწოდოს მრავალი მეთოდის შემცველი დელეგატი. ამისთვის საჭიროა რომ ყველა მეთოდი, რომელსაც იძახებს დელეგატი, არ უნდა აბრუნებდეს არანაირ მნიშვნელობას, ანუ უნდა იყოს void ტიპის. წინააღმდეგ შემთხვევაში დელეგატი დააბრუნებს იმ მეთოდის დაბრუნებულ მნიშვნელობას რომელიც გამოიძახა ბოლოს.
მეთოდის გამოსაძახებლად, რომელიც აბრუნებს void ტიპს, გამოვიყენებ Action<T> ტიპის დელეგატს.
static voidMain(string[] args)
{
Action<double> Ops = MathOps.MultiplyByFour;
Ops += MathOps.Square;
ძველ მაგალითებში შეიძლება შეგხვდეთ ისეთი სიტუაცია, რომელშიც საჭიროა სხვადასხვა დელეგატის გამოყენება სხვადასხვა მეთოდის გამოძახებისთვის. აქ კი შესაძლებელია უბრალოდ ოპერაციების მიმატება და მრავალი მეთოდის შემცველ დელეგატად „გადაქცევა“ ერთი შეხედვით ჩვეულებრივი დელეგატის. დელეგატებთან მუშაობისას, ისინი ცნობენ ისეთ ოპერატორებს როგორებიც არიან + და +=. ასევე დასაშვებია ქვემოთ მოცემული კოდის გამოყენებაც
Action<double> op1 = MathOps.MultiplyByFour;
Action<double> op2 = MathOps.Square;
Action<double> op3 = op1 + op2;
მრავალი მეთოდის შემცველი დელეგატები შესაძლებელს ხდიან გამოიყენოთ - და -= ოპერატორები. ქვემოთ მოცემულია კიდევ ერთი მარტივი გამოყენება მრავალი მეთოდის შემცველი დელეგატებისა და MathOps.cs კლასის განსაზღვრის გზა.
class MathOps
{
public static voidMultiplyByFour(double num)
{
doubleresult = num * 4;
Console.WriteLine("Multiply by 4 {0} = {1}", num, result);
}
public static voidSquare(double num)
{
doubleresult = num * num;
Console.WriteLine("Square of {0} = {1}", num, result);
}
}
შესამჩნევია, რომ MathOps.cs კლასში მოთავსებული მეთოდები სტატიკურია და ისინი მნიშვნელობის დაბრუნების მაგივრად ავტომატურად წერენ პასუხს, რადგან მრავალი მეთოდის შემცველ დელეგატებთან მუშაობისას სასურველია void დაბრუნების ტიპის მეთოდების გამოყენება.
შესაძლებელია ასევე შემდეგი ინტერპრეტაციის გამოყენებაც:
static voidCallAndDisplay(Action<double> action, double num)
{
Console.WriteLine("Called with val = {0}", num);
action(num);
}
ხოლო Main ის კოდი გადაკეთდება შესაბამისად
static voidMain(string[] args)
{
Action<double> ops = MathOps.MultiplyByFour;
ops += MathOps.Square;
CallAndDisplay(ops, 2.0);
CallAndDisplay(ops, 5.21);
CallAndDisplay(ops, 12.322);
Console.ReadKey();
}
კოდის გაშვების შემდეგ გექნებათ შემდეგი შედეგი, თუ რა თქმა უნდა არ შეცვალეთ num ცვლადის მნიშვნელობები.
ასევე გასათვალისწინებელია ის ფაქტიც, რომ როდესაც იყენებთ მრავალი მეთოდის შემცველ დელეგატებს ფორმალურად არ არის განსაზღვრული მეთოდების გამოძახების მიმდევრობა. სასურველია თავიდან აირიდოთ ისეთი მეთოდების გამოძახება მსგავსი დელეგატით, რომელთა გამოძახების მიმდევრობის არევა რაიმე რისკის გამომწვევია, რადგან თუ ერთი დელეგატის გამოძახებისას რაიმე პრობლემა შეიქმნა და კომპილერმა Exception დაიჭირა, მაშინ ამ მეთოდის შემდგომი მეთოდების გამოძახება აღარ გაგრძელდება.
Subscribe to:
Posts (Atom)








