(Για να τρέξει κάποιος το παράδειγμα πρέπει να έχει κατεβάσει το κώδικα της Μ2000, έχει και το m2000.exe μέσα - θέλει Unblock γιατί θα είναι κατεβασμένο από το διαδίκτυο. Μετά ξεκινάει την Μ2000 - αρχικά είναι με μπλε φόντο, άσπρα γράμματα, αλλά μπορεί κανείς να τα αλλάξει με την εντολή Ρυθμίσεις ή με Ctrl+U. Μετά γράφει Σ Α και μπαίνει στο διορθωτή για το τμήμα Α. Εκεί αντιγράφει το πρόγραμμα που είναι παρακάτω, βγαίνει με Esc και γράφει Α και Enter για να το τρέξει. Ειδικά το πλήκτρο Esc κάνει άμεση έξοδο από την εκτέλεση, ενώ το Break κάνει reset στον διερμηνευτή).
Δείτε παρακάτω τι ήθελα να κάνω και τι έκανα! Ήθελα να μεταφέρω ένα πρόγραμμα από C# σε M2000, και όχι κάτι εύκολο αλλά ένα που να χρησιμοποιεί Events γεγονότα και Delegates δηλαδή αντιπρόσωπο (το τι σημαίνει δεν μπορεί κάποιος εδώ άμεσα να το καταλάβει, θα το δούμε παρακάτω)
Για να αντιληφθεί κανείς τι παίζει αρκεί να σκεφτεί ότι έχουμε ένα αντικείμενο που χρησιμοποιεί ένα αντικείμενο Event, "το έχει δημοσιεύσει" σε όρους C#, και σε αυτό του λέει πώς πρέπει να δηλώνεται ένα γεγονός δηλαδή τι παραμέτρους θα παίρνει (μπορεί να πάρει και με αναφορά), και αυτό το κάνει δηλώνοντας μία αντιπροσωπευτική συνάρτηση που απλά δείχνει τι παραμέτρους χρειαζόμαστε. Γιατί το κάνουμε αυτό; Γιατί θα βάλουμε σε αυτό το γεγονός κάποιες συναρτήσεις που θα καλούνται και ο τρόπος που το κάνουμε δεν δηλώνει τι παραμέτρους έχουν οι συναρτήσεις, άρα υποχρεωτικά θα πρέπει να το δηλώσουμε με κάποιο τρόπο. Η C# το κάνει με το Delegate.
Όταν ένα γεγονός συμβεί, στην ουσία, προγραμματιστικά, καλούμε στο αντικείμενο Event την εντολή Raise και περνάμε τις παραμέτρους που θέλουμε. Το αντικείμενο καλεί μια σειρά "subscribers", δηλαδή συναρτήσεις που έχουν καταχωρηθεί ως λήπτες. Τι λαμβάνουν; Λαμβάνουν μια σειρά παραμέτρων, ο κάθε λήπτης τις ίδιες παραμέτρους. Ακόμα και αυτό που περνάμε με αναφορά θα περάσει με αναφορά διαδοχικά απ΄όλους τους λήπτες! Μόλις ολοκληρωθεί η εξυπηρέτηση του γεγονότος, επιστρέφει η εκτέλεση από το σημείο που "προκλήθηκε" το γεγονός.
Στο πρόγραμμα παρακάτω δίνω επεξηγήσεις, και βασίζεται σαν ιδέα σε αυτό εδώ από το tutorials point για τα Event της C#.
Υπάρχουν αρκετές σημειώσεις. Μαθαίνουμε και άλλα πράγματα συνάμα!
Η κλάση event εδώ λέγεται TheEvent επειδή σκοπεύω να την βάλω εσωτερική στη Μ2000 και το event το κρατάω για αντικείμενο.
η παράμετρος με το σύμβολο & μπροστά είναι με πέρασμα με αναφορά. Στην ουσία αντί να περάσουμε την τιμή της μεταβλητής περνάμε σε ένα αλφαριθμητικό την "ισχνή αναφορά", δηλαδή την διεύθυνσή της. Λέγεται ισχνή αναφορά γιατί δεν έχει πραγματοποιηθεί αναφορά ακόμα. Αυτό θα γίνει όταν στο προορισμό την χρησιμοποιήσουμε για να διαβάσουμε ή να γράψουμε στην μεταβλητή. Η Μ2000 μπορεί να χρησιμοποιήσει άμεσα την αναφορά χωρίς να χρησιμοποιήσει όνομα, ή να φτιάξει ένα νέο όνομα που δείχνει εκεί που λέει η αναφορά. Ο πρώτος τρόπος είναι ο εύκολος με την έννοια ότι δεν απαιτεί νέα εγγραφή ονόματος. Ειδικά στις αναφορές συναρτήσεων αντί να περνάμε μια ισχνή αναφορά στη συνάρτηση...περνάμε το κώδικα της συνάρτησης μαζί με στοιχεία που δηλώνουν αν είναι σε αντικείμενο, ώστε οι μεταβλητές του αντικειμένου, ή άλλες συναρτήσεις και τμήματα, ακόμα και τα ιδιωτικά να είναι προσβάσιμα. Μέσα σε αλφαριθμητικό μπορούμε να έχουμε ανώνυμη συνάρτηση, και αυτό ακριβώς κάνουμε στην TheEvent, με το πίνακα αλφαριθμητικών. Η ανώνυμη συνάρτηση όμως περιέχει και κάτι που δηλώνει το περιβάλλον της. Είναι δηλαδή ένα closure με μια έννοια. Δεν είναι όλες οι ανώνυμες συναρτήσεις με δήλωση περιβάλλοντος.
Π.χ.
Τύπωσε συνάρτηση("{=αριθμός**2}",2)
θα δώσει 4
Η ανώνυμη συνάρτηση είναι το αλφαριθμητικό με τις αγκύλες. Η συνάρτηση συνάρτηση() δημιουργεί τοπικά μια συνάρτηση με αυτό το περιεχόμενο, περνάει τον αριθμό 2, την εκτελεί και γυρίζει το αποτέλεσμα.
Στη κλάση TheEvent χρησιμοποιούμε μια κανονική αναφορά, έχουμε φτιάξει την Α() και περνάμε σε αυτήν το πρότυπο των παραμέτρων "message$, &byrefValue". Η κλήση γίνεται χωρίς να πάρουμε κάποιο αποτέλεσμα (δηλώνουμε Κενή). Καλούμε δηλαδή το Α(message$, &byrefValue).
Στο παράδειγμα παρακάτω ανανέωσα την delegate ώστε το κάλεσμα της συνάρτησης να γίνεται σε νέο σωρό. Έβαλα Σημειώσεις 1 και 2 (Σημ 1: και Σημ 2 :) και οδηγίες στην delegate συνάρτηση, για να δει κανείς πως στην κλήση μπορούμε να περάσουμε ή όχι το σωρό τιμών του προηγούμενου επιπέδου!
Κλάση Γενική TheEvent {
Ιδιωτικό:
Πίνακας cast$()
total, param$
Δημόσιο:
Τμήμα TheEvent {
Αν όχι κενό τότε Διάβασε .param$
}
Τμήμα Add {
.total++
Πίνακας .cast$(.total)
Διάβασε .cast$(.total-1)
}
Συνάρτηση Raise {
Συνάρτηση Delegate {
Διάβασε Νέο &Α(), p$
\\ για να διαβάσουμε προκαθορισμένες παραμέτρους
Ένθεση "Διάβασε Νέο "+p$
Σημ 1 : Κάλεσε κενή "Α("+p$+")" \\ περνάμε τον σωρό στην Α()
\\\ ενώ εδώ περνάμε τιμές σε νέο σωρό
\\ βγάλτε τις σημ 1 και 2 και βάλτε μια στην επόμενη γραμμή
\\ θα φανει ότι η κάλεσε περνάει το σωρό
\\ βάλτε πάλι την σημείωση 1 και αφήστε χωρίς σημείωση το 2
\\ και βγάλτε τη σημείωση από εδώ για να δείτε ότι η παρακάτω γραμμή
\\ δεν περνάει το σωρό!
=συναρτηση("Α("+p$+")")
}
Αν .total=0 Τότε Έξοδος
z=.total-1
k=Μέγεθος.Σωρού
{
Αν Μέγεθος.Σωρού>k Τότε Πέτα Μέγεθος.Σωρού-k
Αν z>0 Τότε { Για i=1 Έως k { Πάνω k } }
κάλεσε κενή συνάρτηση Delegate(.cast$(z), .param$)
z--
Αν z>=0 Τότε Κυκλικά
}
}
}
\\ boiler Κλάση
Κλάση Γενική Boiler {
Ιδιωτικό:
temp, pressure
Δημόσιο:
Τμήμα Boiler {
Διάβασε .temp, .pressure
}
Συνάρτηση getTemp {
=.temp
}
Συνάρτηση getPressure {
=.pressure
}
}
\\ TheEvent publisher
Κλάση Γενική DelegateBoilerEvent {
Ομάδα BoilerLogHandler
Τμήμα DelegateBoilerEvent {
.BoilerLogHandler<=TheEvent("message$, &byrefValue")
}
Τμήμα LogProcess {
Διάβασε tempNow
m=1000
remarks$ = "O. K"
b = Boiler(100, tempNow)
t = b.getTemp()
p = b.getPressure()
Αν ( t > 150 ή t < 80 ή p < 12 ή p > 15) Τότε {
remarks$ = "Need Maintenance"
}
Κάλεσε .OnBoilerEventLog("Logging Info:"+ chr$(13)+chr$(10), &m)
Κάλεσε .OnBoilerEventLog("Temparature" + str$(t) + chr$(13)+chr$(10)+"Pressure:" + str$(p), &m)
Κάλεσε .OnBoilerEventLog(chr$(13)+chr$(10)+"Message: " + remarks$, &m)
Τύπωσε m \\ 1006
}
Συνάρτηση OnBoilerEventLog {
Κάλεσε .BoilerLogHandler.Raise()
}
}
Κλάση Γενική BoilerInfoLogger {
Ιδιωτικό:
filename$, fHandler, fHandlerRead
Δημόσιο:
Τμήμα BoilerInfoLogger {
Διάβασε .filename$
Αν Δεν υπάρχει(.filename$) τότε {
Άνοιξε .filename$ Για Ευρεία Εξαγωγή Ως .fHandler
κλείσε #.fHandler
}
Άνοιξε .filename$ Για Ευρεία Συμπλήρωση Ως .fHandler
Άνοιξε .filename$ Για Ευρεία Εισαγωγή Ως .fHandlerRead
}
Συνάρτηση Logger {
Διάβασε info$, &k
k++
Τύπωσε # .fhandler, info$
}
Συνάρτηση NextLine {
Αν όχι Τέλος(#.fHandlerRead ) Τότε {
Γραμμή Εισαγωγής #.fHandlerRead, Paragraph$
Τύπωσε Paragraph$
=Αληθές
}
}
Τμήμα Close {
Κλείσε #.fHandler , #.fHandlerRead
}
}
\\ Η ομάδα RecordBoilerInfo δηλώνει τα γεγονότα
\\ μέσω ενός αντικειμένου DelegateBoilerEvent()
\\ είναι απευθείας γραμμένο σε ομάδα
Ομάδα RecordBoilerInfo {
Συνάρτηση Logger {
Διάβασε Info$, &k
k++
Τύπωσε "to screen:";info$
Σημ 2 : Τύπωσε "stack for values size:", Μέγεθος.Σωρού \\ stack.size
}
Τμήμα Main {
Διάβασε YourLogFile$
filelog = BoilerInfoLogger(YourLogFile$)
\\ δηλώνουμε ένα όνομα παραμέτρου ή περισσότερες
\\ με κόμμα
boilerEvent = DelegateBoilerEvent()
\\------------------------- Προσθέτουμε τους subscribers
\\ η ακολούθους, ή συμβεβλημένους ή όπως θέλετε πείτε το!
\\ μπωρώ να βάλω μια Για boilerEvent.BoilerLogHandler { }
\\ αλλά πρέπει να βάλω την this ή Αυτό στην Logger() αυτού του αντικειμένου
Για boilerEvent.BoilerLogHandler {
\\ το This ή Αυτό, όταν είναι αριστερά (όπως εδώ στην This.Add) ή
\\ όταν χρησιμοπούμε αναφορά &This ή &Αυτό τότε δείχνουμε
\\ στο boilerEvent.BoilerLogHandler
\\ Όμως στο &this.Logger() το this. ή αυτό. που είναι δεξιά
\\ αναφέρται σε αυτό: RecordBoilerInfo, το αντικείμενο
\\ που περιέχει το κώδικα αυτό
This.Add &filelog.logger()
.Add &this.Logger()
}
\\ Μπορώ να χρησιμοποιήσω άμεσα αυτές τις δυο γραμμές:
\\ Δεν χρησιμοποιώ καθόλου το This ή Αυτό
\\ Το &.Logger() μετριέται ως &This.Logger()
\\ αλλά σε μια Για {} θα άλλαζε εκτός αν εμείς γράφαμε ότι είναι το This
\\ boilerEvent.BoilerLogHandler.Add &.Logger()
\\ boilerEvent.BoilerLogHandler.Add &filelog.logger()
\\ -----------------------------------
boilerEvent.LogProcess 12
Μηνύματα()
\\GetAllMessages()
boilerEvent.LogProcess 11
Μηνύματα()
\\ GetAllMessages()
filelog.Close
\\Sub GetAllMessages()
\\ για αγγλικά ονόματα χρησιμοποιούμε το Sub
\\ για ελληνικά το Ρουτίνα ή Ρουτινα
Ρουτίνα Μηνύματα()
Ενώ filelog.NextLine() { }
Τέλος Ρουτίνας \\ ομοίως για την έξοδο
\\End Sub
}
}
\\ Εκκίνηση
Άδειασε \\ αδειάζουμε το σωρό τιμών
Οθόνη \\ καθαρίζουμε την οθόνη
RecordBoilerInfo.Main "c:\boilerNew.txt"
\\ Κλείνει όλα τα αρχεία αν έχει γίνει κάποιο λάθος
Κλείσε 0
Κονσόλα "Del c:\boilerNew.txt";
Πιο μαζεμένη και με αναλογική γραφή! Έβαλα και έναν τρόπο να αλλάζω το \n με αλλαγή γραμμών στο αρχείο και με διαστήματα στην εμφάνιση στην οθόνη!
'Εχω σχεδόν τελειώσει με το Event ως αντικείμενο της Μ2000, και δεν αργεί να βγει η αναθεώρηση 174.
\\ 4 για αναλογική γραφή
Τύπωσε $(4),
Κλάση Γενική TheEvent {
Ιδιωτικό:
Πίνακας cast$()
total, param$
Δημόσιο:
Τμήμα TheEvent {
Αν όχι κενό τότε Διάβασε .param$
}
Τμήμα Add {
.total++
Πίνακας .cast$(.total)
Διάβασε .cast$(.total-1)
}
Συνάρτηση Raise {
Συνάρτηση Delegate {
Διάβασε Νέο &Α(), p$
Ένθεση "Διάβασε "+p$
=συναρτηση("Α("+p$+")")
}
Αν .total=0 Τότε Έξοδος
z=.total-1
k=Μέγεθος.Σωρού
{
Αν z>0 Τότε { Για i=1 Έως k { Πάνω k } }
κάλεσε κενή συνάρτηση Delegate(.cast$(z), .param$)
z--
Αν z>=0 Τότε Κυκλικά
}
}
}
Κλάση Γενική Boiler {
Ιδιωτικό:
temp, pressure
Δημόσιο:
Τμήμα Boiler {
Διάβασε .temp, .pressure
}
Συνάρτηση getTemp {
=.temp
}
Συνάρτηση getPressure {
=.pressure
}
}
Κλάση Γενική DelegateBoilerEvent {
Ομάδα BoilerLogHandler
Τμήμα DelegateBoilerEvent {
.BoilerLogHandler<=TheEvent("message$, &byrefValue")
}
Τμήμα LogProcess {
Διάβασε tempNow
m=1000
remarks$ = "O. K"
b = Boiler(100, tempNow)
t = b.getTemp()
p = b.getPressure()
Αν ( t > 150 ή t < 80 ή p < 12 ή p > 15) Τότε {
remarks$ = "Need Maintenance"
}
Σωρός Νέος {
Κάλεσε .OnBoilerEventLog("Logging Info:\n", &m)
Κάλεσε .OnBoilerEventLog("Temparature" + str$(t) +"\nPressure:" + str$(p), &m)
Κάλεσε .OnBoilerEventLog("\nMessage: " + remarks$, &m)
}
Τύπωσε m \\ 1006
}
Συνάρτηση OnBoilerEventLog {
Κάλεσε .BoilerLogHandler.Raise()
}
}
Κλάση Γενική BoilerInfoLogger {
Ιδιωτικό:
filename$, fHandler, fHandlerRead
Δημόσιο:
Τμήμα BoilerInfoLogger {
Διάβασε .filename$
Αν Δεν υπάρχει(.filename$) τότε {
Άνοιξε .filename$ Για Ευρεία Εξαγωγή Ως .fHandler
κλείσε #.fHandler
}
Άνοιξε .filename$ Για Ευρεία Συμπλήρωση Ως .fHandler
Άνοιξε .filename$ Για Ευρεία Εισαγωγή Ως .fHandlerRead
}
Συνάρτηση Logger {
Διάβασε info$, &k
k++
Τύπωσε # .fhandler, αλλαγη$("\n",chr$(13)+chr$(10), info$);
}
Συνάρτηση NextLine {
Αν όχι Τέλος(#.fHandlerRead ) Τότε {
Γραμμή Εισαγωγής #.fHandlerRead, Paragraph$
Τύπωσε Paragraph$
=Αληθές
}
}
Τμήμα Close {
Κλείσε #.fHandler , #.fHandlerRead
}
}
Ομάδα RecordBoilerInfo {
Συνάρτηση Logger {
Διάβασε Info$, &k
k++
Πένα (πενα+9) υπολ 16 {
Τύπωσε "to screen:";Αποκ$(Αλλαγή$("\n"," ", info$))
}
Σημ 2 : Τύπωσε "stack for values size:", Μέγεθος.Σωρού \\ stack.size
}
Τμήμα Main {
Διάβασε YourLogFile$
filelog = BoilerInfoLogger(YourLogFile$)
boilerEvent = DelegateBoilerEvent()
boilerEvent.BoilerLogHandler.Add &.Logger()
boilerEvent.BoilerLogHandler.Add &filelog.logger()
boilerEvent.LogProcess 12
GetAllMessages()
boilerEvent.LogProcess 11
GetAllMessages()
filelog.Close
Sub GetAllMessages()
Ενώ filelog.NextLine() { }
End Sub
}
}
Οθόνη
RecordBoilerInfo.Main "c:\boilerNew.txt"
Κονσόλα "Del c:\boilerNew.txt";