ΘΕΡΜΑΝΣΗ - ΚΛΙΜΑΤΙΣΜΟΣ - ΜΗΧΑΝΟΛΟΓΙΚΑ
ΘΕΡΜΑΝΣΗ - ΚΛΙΜΑΤΙΣΜΟΣ - ΜΗΧΑΝΟΛΟΓΙΚΑ
Home | Profile | Active Topics | Active Polls | Members | Search | FAQ
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 2. Τεχνικά Forum.
 Software
 Προγραμματισμός με object Pascal (Delphi)
 Forum Locked
 Printer Friendly
Author Previous Topic Topic Next Topic  

pfys
New Member

Greece
105 Posts

Posted - 25 July 2012 :  20:18:59  Show Profile
Σκέφτομαι να γράψω κάποια κείμενα για αντικειμενοστρεφή προγραμματισμό σε object pascal (Delphi). Δεν σκοπεύω να περιγράψω την γλώσσα αλλά να διατυπώσω κάποιους κανόνες και πρακτικές που έχω καταλήξει.

Μια πιθανή λίστα θεμάτων είναι η ακόλουθη.
1. Κλάση & αντικείμενα (Class vs Object). Πως και γιατί.
2. Object encapsulation, πολυμορφισμός, κληρονομικότητα.
3. Τυπική εφαρμογή, ενημέρωση της οθόνης και χρονοβόρες διαδικασίες.
4. TCP/IP sockets
5. Διεργασίες - Νήματα (Threads) σαν κομμάτι επεξεργασίας.
6. Πολυνηματικός TCP/IP Server
7. Modbus επικοινωνία μέσω TCP/IP
8. Σειριακή επικοινωνία. Διαγράμματα αλλαγής κατάστασης.

Υπάρχει ενδιαφέρον ?

stom
Senior Member

Greece
1665 Posts

Posted - 26 July 2012 :  19:51:54  Show Profile
Πανε πολλα χρονια που εχω να δω Delphi και πιθανοτατα εχω χασει επεισοδια.

Παρολα αυτα, οι τεχνικες παραμενουν τεχνικες..

πχ modbus?



Burn baby, burn
Go to Top of Page

dipoli
Administrator

Greece
4047 Posts

Posted - 26 July 2012 :  20:09:27  Show Profile  Visit dipoli's Homepage
Σιγουρα εγώ και πιθανότατα και ο Καπετάνιος θα ενδιαφέρεται.

Και γώ έχω να χρησιμοποιήσω τη γλώσσα απο το 1995 οπότε μιλάμε για πλήρη άγνοια....




I believe we should all pay our tax with a smile. I tried - but they wanted cash...
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 26 July 2012 :  21:15:36  Show Profile
Πάνω που είχα αρχίσει να απογοητεύομαι.

Όλη η αναφορά σε TCP/IP και πολυνηματικούς servers είναι παραγγελιά από τον καπετάνιο. Από τα υπόλοιπα κάποια είναι κοινά για όλες τις OO γλώσσες και κάποια ειδικά σε Win32 με Delphi.

Υπάρχει μια μεγάλη αλλαγή στο πως γράφεις προγράμματα, σειριακά με C ή με Fortran και όταν χρησιμοποιείς objects. Αλλάζει ο τρόπος που σκέφτεσαι. Το ισοδύναμο που μπορώ να βρω είναι τα κλειστά κουτιά στα ΣΑΕ, με συγκεκριμένους εισόδους, λειτουργικότητα (συνάρτηση μεταφοράς) και εξόδους.
Πολλά τέτοια κουτιά, συνδεδεμένα μεταξύ τους υλοποιούσαν το σύστημα.

Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 27 July 2012 :  00:17:08  Show Profile
1. Κλάση & αντικείμενα
Aντικειμενοστρεφής προγραμματισμός είναι η διαδικασία σχεδίασης και υλοποίησης προγραμμάτων με βασική μονάδα την κλάση. Γνώρισε αποδοχή γιατί η M$oft, στα μέσα της δεκαετίας του 90, έβγαλε ένα πολύ καλό C++ compiler και προώθησε την C++ σαν γλώσσα για το COM, η Sun διέθεσε την Java σαν open source και η Borland προσπάθησε να λάμψει ξανά βγάζοντας στην αγορά σαν VB killer, το Delphi.

Η κλάση (class) είναι μία προδιαγραφή / ένα πρότυπο και δηλώνει την συμπεριφορά ή την λειτουργικότητα που μπορεί να παράσχει. Έχει ενσωματωμένα χαρακτηριστικά (μνήμη), ορατά ή όχι στους χρήστες της. Επιπλέον εκτελεί ενέργειες (μεθόδους), που χρησιμοποιούν την εσωτερική μνήμη και υλοποιούν την λειτουργικότητα της.

Έχει τέσσερα βασικά χαρακτηριστικά

Abstraction : επιτρέπει την δημιουργία αφαιρετικών μοντέλων
Encapsulation: συνδέει δεδομένα και λειτουργίες.
Κληρονομικότητα (Inheritance) : είναι η δυνατότητα μιας κλάσης να κληρονομήσει δεδομένα και μεθόδους μιας άλλης προγενέστερης κλάσης.
Πολυμορφισμός (Polymorphism): η δυνατότητα μιας κλάσης να κληρονομήσει και να μεταβάλει τις συμπεριφορές μιας άλλης ανώτερης κλάσης.

Κάθε κλάση ορίζει μία μέθοδο δημιουργίας (constructor) μέσω της οποίας δημιουργούνται αντικείμενα. Το αντικείμενο υλοποιεί τις ιδιότητες του προτύπου (class).

Τα αντικείμενα αλληλεπιδρούν και ανταλλάσουν δεδομένα μεταξύ τους για να δημιουργήσουν τελικά ένα πρόγραμμα.

Στο Delphi τα objects δημιουργούνται πάντα δυναμικά και η αναφορά τους είναι ένας pointer. Ο compiler εξάλειψε την ανάγκη της χρήσης του ^ κατά την χρήση των objects.

Ορίζει επιπλέον και μία μέθοδο καταστροφής (destructor) του αντικειμένου που καλείται όταν παύει να χρησιμοποιείται και αποδεσμεύσει την μνήμη που χρησιμοποίησε..

Ας δούμε την κλάση ΤStream. http://delphi.about.com/od/vclusing/l/aa110803a.htm
Το TStream είναι αφηρημένο και ανύπαρκτο. Οι κλάσεις TMemoryStream και TFileStream, που κληρονομούν τις ιδιότητες του, υλοποιούν την δυνατότητα να διαβάζουνε ή να γράφουνε σε μνήμη ή σε αρχείο αντίστοιχα.

Στο http://www.swissdelphicenter.ch/torry/showcode.php?id=822 δέστε την δήλωση της procedure CompressStream(inpStream, outStream: TStream);
Διαβάζει δεδομένα από ένα stream στην μνήμη, τα συμπιέζει στην μνήμη και τα γράφει σε ένα άλλο stream.

Μια πρώτη προφανής βελτίωση είναι η απευθείας χρήση TFileStream που λειτουργεί απευθείας με αρχεία και δεν απαιτεί να διαβάζουμε πρώτα το περιεχόμενο στη μνήμη (x 1) να το αντιγράφουμε στην μνήμη (x2) να το συμπιέζουμε στην μνήμη (x 3) και στην συνέχεια να το γράφουμε στο δίσκο.

var fs1, fs2: TFileStream;
begin
fs1 := nil;
fs2 := nil;
try
fs1:=TFileStream.Create('c:\fs1.dat',fmOpenRead or fmShareExclusive);
fs2:=TFileStream.Create('c:\fs2.dat',fmOpenWrite or fmCreate);
CompressStream(fs1, fs2);
finally
fs2.Free;
fs1.Free;
end;
end;

Στο http://pages.cs.wisc.edu/~rkennedy/vmt αναλύεται πως επιτυγχάνεται πολυμορφισμός και μπορούμε να δηλώνουμε την Compress με ορίσματα τύπου TStream και να την χρησιμοποιούμε με ορίσματα τύπου TMemoryStream & TFileSTream.

Τι χρειάζεται ώστε ένα TFilestream να μπορεί να συμπιέζει όταν γράφει ή αντίθετα ?
http://www.koders.com/delphi/fidD3C85C433D21324BD9C3C67620B9AAC15D243B9D.aspx?s=zip μας απαντάει το ερώτημα.

Πως αναγνωρίζουμε objects, μεθόδους και χαρακτηριστικά (attributes/properties).
Ξεκινάμε με μία σύνοψη που περιγράφει το πρόβλημα. Τα ουσιαστικά, είναι υποψήφια objects. Αν υπάρχουν και στην πραγματικότητα τότε τα σιγουρεύουμε.
Οι ιδιότητες ή τα χαρακτηριστικά τους είναι attributes (μνήμη) και οι μέθοδοι απαντούν σε μηνύματα και επενεργούν στη μνήμη του object.

Στις 16 Σεπτεμβρίου 1998 οι Rolling Stones εμφανίστηκαν στο ΟΑΚΑ.
Είχα δουλέψει με τον PCL (πρόκειται μάλλον για επαγγελματική διαστροφή να αναφέρεσαι σε φίλο με το user name) για την προετοιμασία του λογισμικού υποστήριξης της συναυλίας.
Το project αφορούσε 2 συναυλίες, στο ΟΑΚΑ και στο Λιμάνι στην Θεσσαλονίκη.

Τα εισιτήρια της συναυλίας ήταν προτυπωμένα με ολόγραμμα και barcode, σε αριθμημένα μπλοκάκια των 100 εισιτηρίων. Η εταιρεία …. ανέλαβε την διάθεση των εισιτηρίων και μέσω του call center της, επέτρεπε την κράτηση θέσεων σε διαφορετικές ζώνες του γηπέδου και με διαφορετική τιμή, πληρωμή με πιστωτική κάρτα, εμφακέλωση των εισιτήριων, αποστολή στους παραλήπτες, επικοινωνία και επαναποστολή σε περίπτωση αποτυχίας παράδοσης και τελικά έλεγχο του εισιτηρίου (μέσω του barcode) κατά την είσοδο στο γήπεδο.

Αναφέρω κάποιες κλάσεις που προκύπτουν από αυτή τη μικρή περιγραφή.
1. Το Venue (θέατρο, γήπεδο) που χωρίζεται σε θύρες/ζώνες. Κάθε θύρα έχει θέσεις, αριθμημένες ή όχι (αγωνιστικός χώρος) και μία τιμή.
2. Η παράσταση (Show), με την ημερομηνία, περιγραφή και το γήπεδο/θεάτρο.
3. Το block των εισιτηρίων με αναφορά σε ζώνη, τιμή εισιτηρίου και από-έως αριθμό εισιτηρίου.
4. Τα εισιτήριά που αντιστοιχούν σε θέση
5. Ο θεατής, με την διεύθυνση του, τον αριθμό της κάρτας του.
6. Την κράτηση ενός θεατή, με ημερομηνία κράτησης, παράσταση, ζώνη, αριθμό θέσεων και κόστος.
7. Τις θέσεις μιας κράτησης.

Η θέση του γηπέδου πρέπει να είναι μία κλάση και κάθε θέση ένα object ? (Υπάρχουν σπασμένες θέσεις ?)

Κάθε κλάση μπορεί να αποτελείται από άλλες (πχ Γήπεδο, θύρα, θέση) ή να συνδέεται με κάποια άλλη (πελάτης με κράτηση).

Οι κλάσεις αυτές αναφέρονται σαν PDC (Problem Domain Components).

Το αποτέλεσμα. http://www.tanea.gr/oikonomia/article/?aid=4034166
Εγώ πήγα αυθημερόν στις Βρυξέλες και είδα τον Larry Elison να μιλάει για το όραμα της Oracle αντί να ακούσω το Angie.

Πως αναγνωρίζουμε τις μεθόδους
Είναι οι λειτουργίες μια κλάσης. Ξεκινούμε από τις βασικές λειτουργίες και καταλήγουμε στις ειδικές .

Έστω ότι έχουμε την παρακάτω κλάση

TSex = (sxMale,sxFemale);

TMammal = class
private
fAge : double;
fWeight:double;
fHeight:double;
fSex:TSex;
public
constructor Birth;
destructor Die;
procedure Sleep;
procedure Work;
procedure Eat;
property Age : double read fAge write fAge;
property Weight:double read fWeight write fWeight;
property Height:double read fHeight write fHeight;
property Sex:TSex read fSex write fSex;
end;

THuman = class(TMammal)
public
procedure Vote;
end;

Είναι βέβαιο ότι ο καθένας θα μπορούσε να προσθέσει και πολλές άλλες λειτουργίες και χαρακτηριστικά. Η κλάση είναι μια προδιαγραφή. Επεκτείνεται και μεταβάλλεται.
Στην κορυφή της κληρονομικής ιεραρχίας έχουμε πάντα τις γενικές λειτουργίες και προχωρώντας προς τα φύλλα καταλήγουμε στην εξειδίκευση των λειτουργιών.

Οθόνη καταχώρησης – απεικόνισης
Στη συνέχεια πρέπει να σχεδιάσουμε τις οθόνες για την καταχώρηση των χαρακτηριστικών κάθε κλάσης - οντότητας. Οι ΑΠΛΕΣ οθόνες έχουν λίγα πεδία και λίγα χρώματα και χρησιμοποιούν ένα font χωρίς υπερβολές. Το απλό είναι δύσκολο.

Οι ανάγκες της εμφάνισης των δεδομένων είναι ανεξάρτητες από τα χαρακτηριστικά της κλάσης . Θα δημιουργήσουμε μία φόρμα για κάθε κλάση που θέλουμε να ενημερώσουμε.

Οι φόρμες κλάσεις αναφέρονται σαν HIC (Human Interaction Components). Το βρίσκω ισοδύναμο με τις HMI οθόνες /συσκευές που εμφανίζουν τα δεδομένα που συλλέγει το plc.

Για το γήπεδο θα υπάρχει η ΤVenueForm με γραφικά και δυνατότητα επιλογής θύρας.
Η φόρμα θα δέχεται δεδομένα, θα τα ελέγχει για ορθότητα, και θα δημιουργεί μια PDC και ενημερώνει τα πεδία της.

Πως από την κλάση πάμε στη database ?
«Υπάρχει η Oracle και text files». Το είχε πει πριν από χρόνια, το 1998, ο Βασίλης και παραμένει δυστυχώς αλήθεια. Από την άλλη η M$oft με τον sqlserver δημιούργησε την db για τις μάζες. Έγραφες ένα select * from table και τα αποτελέσματα εμφανιζόταν σε ένα εξαιρετικό εργαλείο. Κανένας δεν νοιαζόταν για performance και ότι οι writers μπλοκάρουν τους readers.

Αρχικά αντιστοιχούμε κάθε κλάση σε ένα πίνακα. Κάθε πίνακας πρέπει να έχει πρωτεύον κλειδί. Χρησιμοποιούμε, ένα χαρακτηριστικό που είναι μοναδικό (πχ ΑΦΜ σε πελάτες) ή ένα κρυφό τεχνητό κλειδί (surrogate key). Επιλέξτε ένα μικρό κλειδί (ένα 32 bit integer). Μην χρησιμοποιείτε Guids. Σταματήστε την άσκοπη χρήση χώρου και υπολογιστική ικανότητας.
Οι πίνακες συνδέονται μεταξύ τους με κλειδιά (Foreign Keys). Χρησιμοποιήστε τα. Δηλώστε στη βάση όλους τους κανόνες ή περιορισμούς (constraints) που ορίζονται για το δεδομένα σας.

Η DMC κλάση (Data Management Component) επικοινωνεί με μία PDC κλάση και παράγει τις κατάλληλες SQL εντολές για να ενημερώσει την database.

H DMC κλάση «τρέχει» δίπλα στην database, παράγει 1..Ν sql εντολές και μέσω του database client (Oracle OCI ή M$oft dblib) τις στέλνει με TCP/IP προς εκτέλεση στον database server.
Οι καταχωρήσεις στην βάση γίνονται πάντα σε επίπεδο οντότητας και να προστατεύεται η ακεραιότητα των δεδομένων χρησιμοποιούνται transactions.
Τα transactions έχουν ΑΥΣΤΗΡΑ μικρή διάρκεια. Δηλαδή, δεν εμφανίζουμε ερωτήσεις ενώ έχουμε ξεκινήσει ένα transactions ή δεν εκτυπώνουμε μέσα σε transaction.

Αποφεύγουμε τα deadlocks. Αν σε ένα ενεργό transaction ενημερώνουμε τις εγγραφές με PK A και Β και σε ένα δεύτερο ταυτόχρονο transaction τις B και Α τότε το ένα transaction θα κλειδώσει το άλλο.

Κάποια στιγμή μου ζητήσανε βοήθεια για ένα πρόβλημα deadlock που εμφανιζόταν σε μία Oracle. Δύο σταθμοί καταχωρούσαν μεγάλες παραγγελίες και που & που κωλούσαν.
Ρώτησα αν ταξινομούσαν τις γραμμές τις παραγγελίας πριν την καταχώρηση.

Client Server ή 3 Tier
Η εφαρμογή μας περιέχει τις HIC, PDC & DMC κλάσεις και τρέχει σένα PC. Ένα δίκτυο 100 Mbits μας συνδέει με την βάση δεδομένων. Όταν λοιπόν εισάγουμε στην οθόνη 1 κράτηση με 100 θέσεις θα πρέπει να την καταχωρήσουμε στην βάση με χρήση 101 εντολών “insert into table column list values list” μέσα σε ένα begin transaction & commit transaction.

Τι γίνεται όταν η εφαρμογή μας τρέχει στην Κομοτηνή και η database είναι στον Άγιο Στέφανο. Θα πρέπει η εφαρμογή μας να σπάσει σε HIC & PDC που «τρέχουν» στην Κομοτηνή και το DMC να βρεθεί δίπλα στην database στον Άγιο Στέφανο.

Η διαχειριστική εφαρμογή δέχεται ή παράγει δεδομένα, σχηματίζει ένα μήνυμα με αυτά (κωδικοποιημένο πιθανά σαν xml) και το στέλνει στο PDC στο Άγιο Στέφανο.
Εκεί, ένας xml to sql maper http://stackoverflow.com/questions/1702535/xml-to-sql-mapping καταχωρεί τα δεδομένα στην βάση δεδομένων και ενημερώνει τον χειριστή για την επιτυχία / αποτυχία της καταχώρησης.

«Στέλνει το μήνυμα στον Αγιο Στέφανο». Ποιος όμως το παραλαμβάνει ?

Application Server
Μιλάμε για έναν application server, αν χρησιμοποιούμε δικά μας μηνύματα ή ένα Web Server που υποστηρίζει http πρωτόκολλο (internet) με xml περιεχόμενο.

Ξαφνικά ένας open source Apache Web Server μπορεί άνετα να χρησιμοποιηθεί (ξέρω, έχουμε φύγει λίγο από το Delphi).

Ti κοινό έχουν ένας application Server, o Web Server και ο database server. Ακούνε σε μία πόρτα για συνδέσεις, δέχονται πολλαπλές συνδέσεις μέσω TCP/IP από πελάτες που ξέρουν να τους μιλήσουν, δημιουργούν ένα ανεξάρτητο νήμα (thread) εξυπηρέτησης κάθε σύνδεσης και ανταλλάσουν μηνύματα με το πελάτη για όσο διάστημα διαρκεί η σύνδεση.

Ένας Delphi application server δέχεται custom μηνύματα, και στο thread του πελάτη (Κομοτηνή) δημιουργεί το κατάλληλο PDC object που μεταφράζει το μήνυμα σε sql και τα καταχωρεί στην βάση. Ένας Apache δέχεται http μηνύματα, αποσπά το xml και μέσω php το καταχωρεί στην βάση. Ένας database server, δέχεται μηνύματα μέσω του database client, και μπορεί να εκτελέσει sql εντολές.

The Delphi Way
Το Delphi προχώρησε ένα βήμα παραπάνω επεκτείνοντας την γλώσσα Pascal ώστε η γραμματική της να επιτρέπει την δήλωση κλάσεων και πρόσθεσε ένα εξαιρετικά παραγωγικό και γρήγορο περιβάλλον εργασίας για την δημιουργία προγραμμάτων. Υποστήριξε μάλιστα και κληρονομικότητα στις φόρμες της εφαρμογής. Με χρήση data aware components μπορείς, ξεχνώντας όλα τα παραπάνω, να μεταβάλεις απευθείας τα δεδομένα της βάσης.
Tragic.
Όλοι μπορούσαν να φτιάξουν πολύ γρήγορα διαχειριστικές ή άλλες εφαρμογές. Χωρίς ανάλυση ή μεθοδολογία. Η κάθε φόρμα, έβλεπε τις υπόλοιπες, ο κώδικας χειρισμού της οθόνης, έμπλεκε με τους υπολογισμούς και με την αποθήκευση/ανάκληση από τον βάση δεδομένων. Μια μεταβολή σε κάτι φαινομενικά απλό επιδρούσε σε κάτι άσχετο.

Ρίξτε μια ματιά σε ένα τελευταίο σας project. Μετρήστε τις απλές ρουτίνες, έξω από objects και τις global μεταβλητές. Τι θα σας προσφέρουν τα objects. Δυσκολότερη αρχική υλοποίηση (χρειάζεται σκέψη), ευκολότερη συντήρηση, μεγαλύτερη επεκτασιμότητα λιγότερα bugs.
Go to Top of Page

Pirate
Advanced Member

Greece
3177 Posts

Posted - 27 July 2012 :  11:53:23  Show Profile  Visit Pirate's Homepage
Μιας και γράφω από pda διότι:
«πρώτο καλοκαίρι μετά από 18 χρόνια που δεν πήρα το portable παραμάσχαλα στις διακοπές, άρα Νίκη μεγάλη...»
θέλω να πω ένα μπράβο στον pfys για την ανάπτυξη αυτή που δείχνει ότι μπήκε με 1000 στο OOP, αλλά ταυτόχρονα και το επικήδειο ρέκβιεμ για μια πολύ μεγάλη γλώσσα προγραμματισμού την οποία πεθάνανε με το ζόρι προς δόξα άλλων όχι αναγκαστικά καλύτερων...

Του αφιερώνω αυτό από τον καιρό του 1993 όταν διάβαζα όλες αυτές τις στρυφνές έννοιες μόνος και δίχω δάσκαλο...
OOP & Ελληνική γλώσσα σε όλο της το μεγαλείο

και το παρατηρείστε το ωραιότερο:
Προς Αμερικανούς αναγνώστες: Τι σημαίνει Polymorphism...
Polymorphism is Greek "for many shapes" and it is just that: a way of giving an action...

Τι σημαίνει Πολυμορφία;
Για τους Έλληνες δεν χρειάζεται ιδιαίτερη εξήγηση τι σημαίνει πολυμορφία. Από πολλές πηγές μας αν ανατρέξουμε π.χ. στο δωδεκάθεο θα δούμε ότι οι Θεοί έπαιρναν συνήθως διαφορετικές μορφές στην επικοινωνία τους με τους θνητούς. Μπορούμε να πούμε ότι είχαμε πολύμορφους θεούς κι απ' ότι φαίνεται αυτή η ιδιότητα άρεσε στον κόσμο.
Για τα Delphi objects η πολυμορφία είναι μία ζηλευτή ιδιότητα όπου επιτρέπει στο ίδιο αντικείμενο με τα κοινά χαρακτριστικά π.χ. στη γάτα κλάσης TGata να περιέχει μία ακόμη ιδιότητα π.χ. KaloGataki η οποία είναι τύπου TKaloGataki ο οποίος παίρνει π.χ. τις τιμές: kgGourgourizon, kgKakistro, kgGriniazon, kgKleftizon
Συνεπώς το ίδιο το object που περιγράφει μια γάτα, μπορεί να συμπεριλάβει πολλούς τύπους γατιών διότι encapsulates εκτός των άλλων π.χ. χρωματισμών τριχώματος, χρωματισμό ματιών κλπ. μία ακόμα ιδιότητα που είναι τύπου «καλό γατάκι».
Από κει και μετά έρχεται η κληρονομικότητα η οποία σου επιτρέπει να φτιάξεις μία άλλη κλάση γάτας πχ την TSiamGata η οποία είναι κλάσης TGata και επομένως μονομιάς κληρονομεί όλες τις ιδιότητες αυτής επομένως και την KaloGataki. Ταυτόχρονα επιτρέπει στον προγραμματιστή να προσθέσει στη νέα κλάση ιδιότητες που δεν βλέπει η πρόγονη κλάση. Για παράδειγμα την ιδιότητα:
Oura τύπου TOura ο οποίος παίρνει τις τιμές: gStraviOura, gHorisOura, gNormalOura

Περιττό να προσθέσω ότι η ιδέα των objects είναι απόρροια των φοβερών records της Pascal. Το ηλιακό μου πρόγραμμα βασίσθηκε σε 3 τέτοια records που δόμησα όπως TSunData, THliakosStruc, TBoilerStruc. Για παράδειγμα η θερμοκρασία εξόδου από τον συλλέκτη είναι ιδιότητα του THliakosStruc ή η θερμοκρασία εισόδου στον εναλλάκτη του boiler είναι ιδιότητα του TBoilerStruc

Δυστυχώς όμως.... Σ' αυτόν τον κόσμο τον κακό, τον χιλιομπαλλωμένο, ό,τι καλό πεθαίνει!


Φευγάτος Πειρατής


Στίγμα Μαστροκαπετάνιου

Edited by - Pirate on 27 July 2012 11:57:50
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 27 July 2012 :  15:10:10  Show Profile
Πραγματικός κόσμος
Θέλω να βάλω ένα καλοριφέρ στο σπίτι μου. Πέντε σωματάκια και ένα μπόιλερ θα βάλω. Έχω μάστορα διαμάντι. Γιατί να πληρώσω τον μηχανολόγο ?

Ακούγεται γνώριμο ?. Ισχύει παντού. Η μεθοδολογία υπάρχει και αν θέλουμε την χρησιμοποιούμε. «Τι έχεις καρδιά μου και αναστενάζεις. Το κεφάλι τα κάνει το κεφάλι τα τραβάει» όπως μου έλεγε, από μικρό, ο πατέρας μου.

Έστω ότι πρέπει να καταχωρήσουμε τα στοιχεία ενός πελάτη σύμφωνα με την μεθοδολογία. Έχουμε ετοιμάσει την PDC κλάση ΤCustomer που περιγράφει το τι είναι ο πελάτης.


TCustomer = class
private
FCUSTID:Integer;
FFNAME:String;
FLNAME: String;
FLASTPICKUP: String;
FAFM: String;
FADDRSTR: String;
FADDRNUM: String;
FADDRZIP: String;
FADDRCITY: String;
FTEL1: String;
FTEL2: String;
FCOMPANY: String;
FPROFESSION: String;
FDOY: String;
Protected
// δημιουργεί νέα Ιd για τον πελάτη
procedure SetCustId(value:integer);
// Ελέγχει αν το ΑΦΜ που καταχωρούμε είναι σωστό.
procedure SetΑFM(const value:string);

public
Property CUSTID:Integer read FCustId write SetCustId;
Property FNAME:String read FFName write FFname;
Property LNAME:String read FLName write FLname;
Property AFM:String read FAFM write SetAFM;
...
End;


Έχουμε ετοιμάσει και την DMC κλάση
TDMCustomer =class(ΤCustomer)
Public
// Δημιουργεί ένα πελάτη και ανακαλεί τα στοιχεία του από την βάση.
Constructor Create(ID:integer);
Procedure Save;
Procedure Update;
End;

Έχουμε σχεδιάσει την οθόνη καταχώρησης

https://picasaweb.google.com/107823581317170981756/July272012?authkey=Gv1sRgCKmIkMi845uYSA#5769803951973481138

Η TCustForm επικοινωνεί με την ΤCust για να εμφανίσει ή να μεταβάλει τα στοιχεία ενός πελάτη. Η TCust με την TDMCust για να ανακαλέσει ή να αποθηκεύσει τα δεδομένα στην βάση δεδομένων.

Ας δούμε τις «ευκολίες» του Delphi. Τα data aware components επιτρέπουν

1. Την σύνδεση με πίνακα της βάσης και ορισμό των χαρακτηριστικών της γιαλαντζί κλάσης, από όλα τα πεδία της βάσης.
2. Την δυνατότητα μεταβολής των δεδομένων
3. Την αυτόματη ενημέρωση της βάσης.

Ας αφήσουμε τις διαχειριστικές εφαρμογές και ας δούμε την εφαρμογή του πειρατή.



Τα ουσιαστικά δηλώνουν τα objects. Πολύ περισσότερο όταν υπάρχουν και φυσικά. Τα ρήματα δηλώνουν μεθόδους/λειτουργίες.

Ο ήλιος ακτινοβολεί συγκεκριμένη ενέργεια ανάλογα με την περιοχή και την ώρα.

TSunEnergy = class
Public
Function EnergyFlow:Double; // ακτινοβολεί..
Property Month
Property Day
End;

Ο ηλιακός συλλέκτης αποφορά την ενέργεια, και την μετατρέπει σε θερμική

TSolarPanel = class
public
property Slope :double; //κλίση
property SouthDeviation:double; // Απόκλειση από τον Νότο
property Surface:double; // συλλεκτική επιφάνεια
property Flow:double; // Lt/h
property GlycolePerc : double;
end;

Το boiler μέσω του εναλλάκτη για τον ηλιακό, ζεσταίνει το νερό που περιέχει

ΤBoiler = class
public
property Volume:double;
property AverageDaylyConsuption:double;
property WaterTemperature:double;
property SolarHeatExchangeSurface :double; // m2
end;

H κλάση TCalculator υπολογίζει
ένα σύνολο τιμών,

για μία ημερομηνία aDate,
από ώρα FromTime έως ToTime με βήμα step λεπτά
για τον συλλέκτη aSolarPanel
και το boiler aBoiler.


TCalculator = class
procedure Calc(aDate:TDate; // για συγκεκριμένη ημερομηνία
FromTime, ToTime:TTime; step: integer;
aSolarPanel:TSolarPanel;
aBoiler:TBoiler);
property CalcCount:integer;
property Time[index:integer]:TTime; //
property SunHeight[index:integer]:degrees;
property SunAzimou8io [index:integer]:degrees;
property BoilerTemp [index:integer]:double;
property CollectorTempIn [index:integer]:double;
property CollectorTempOut [index:integer]:double;
...
end;


Τα αποτελέσματα εμφανίζονται στην οθόνη

For i:=0 to Calculator.CalcCount do
begin
Grid[0,i]:= FormatTime(Calculator.Time[i]);
Grid[1,i]:= FormatDegrees(Calculator.SunHeight [i]);
Grid[2,i]:= FormatDegrees(Calculator.SunAzimou8io [i]);
Grid[3,i]:= Format('%.1f',[Calculator.BoilerTemp [i]]);
Grid[4,i]:= Format('%.1f',[Calculator.CollectorTempIn[i]]);
Grid[5,i]:= Format('%.1f',[Calculator.CollectorTempOut[i]]);
...
End;

κάθε κλάση ορίζεται σε ξεχωριστό unit. Η φόρμα γνωρίζει μόνο τον Calculator.


Εδώ, συνάδελφοι μηχανολόγοι, χρειάζομαι την βοήθεια σας για το ολοκληρώσουμε το μοντέλο. Χρειάζομαι τις προτάσεις σας για τα επιπλέον χαρακτηριστικά των κλάσεων και τις μεθόδους/υπηρεσίες που παρέχουν.

ΥΓ.
1. Εγραφα και αφου τελείωσα είδα την απάντηση του πειρατή. Προκύπτει απόλυτη σύμπτωση στην ανάλυση. Μπορεί να τον καταφέρω να το ξαναγράψει με objects

2. Περίμενα οτι θα υπήρχαν κάποιοι, που θα διαφωνούσαν με τον ορισμό του THuman με μόνο λειτουργία την ψήφο. Υπήρχαν άλλες πολύ πιο σημαντικές (Ερωτας, διανόηση, επικοινωνία).

Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 27 July 2012 :  15:30:23  Show Profile
quote:

«πρώτο καλοκαίρι μετά από 18 χρόνια που δεν πήρα το portable παραμάσχαλα στις διακοπές, άρα Νίκη μεγάλη...»



Δεν φαντάζομαι, η μεγάλη Νίκη να είναι κάποια παλιά συμμαθήτρια ?
Go to Top of Page

Pirate
Advanced Member

Greece
3177 Posts

Posted - 27 July 2012 :  17:21:12  Show Profile  Visit Pirate's Homepage
quote:
Δεν φαντάζομαι, η μεγάλη Νίκη να είναι κάποια παλιά συμμαθήτρια ?

Πάλι θα απαντήσω από pda γιατί «ξέρεις να με προκαλείς»...
1. Η Νίκη η συμμαθήτρια πάει, χάθηκε! Και καλύτερα που χάθηκε για να μη με δει έτσι όπως με κατάντησε το άλλο μου πάθος (δείγμα παραπάνω) με την 25χρονη παραμονή μου σε καρέκλα μπροστά από τον δαίμονα που λέγεται PC. Εκτοξεύθηκε ο νους και γκρεμοτσακίσθηκε το σώμα! Συν 60 κιλά! Καλύτερα να μη με δει η Νίκη διότι θα χάσει πάσα ιδέα για τον γνώριμό της και πολυαγαπημένο συμφοιτητή που ήξερε να την ταξιδεύει δίχως τα objects της Pascal μιας και τότε δεν τα γνώριζα καν.
ΠΡΟΣΟΧΗ λοιπόν σε σας τους νεώτερους. Πρώτα το σώμα και μετά ο νους! Καμία γνώση δεν είναι αυτοσκοπός όσο και να μας καταξιώνει. Άλλωστε εμείς οι Έλληνες το γνωρίζουμε καλά με το γνωστό «νους υγιής εν σώματι υγιή»
2. Η Νίκη για την οποία μιλάω είναι κι αυτή θυλυκού γένους και θα μπορούσε να είναι μια ωραία γυναίκα για να μου κεντρίσει το ενδιαφέρον να κλείσω πλέον τον «δαίμονα». Το έχω βάλει στοίχημα με τον εαυτό μου και θα το πετύχω. Θέλω να πάρω πίσω από τα 60 κιλά όσα μπορέσω κι ας μείνουν όσα μείνουν. Ικανά όμως να έλξουν την άλλη Νίκη, την παλιά μου συμμαθήτρια

Συνεπώς:
quote:
Μπορεί να τον καταφέρω να το ξαναγράψει με objects

Κάνε λίγο υπομονή. Έχω τον γιο μου πρωτοετή στο ΕΜΠ σπασικλάκι κανονικό με δεκάρια κι έτσι. Και στον προγραμματισμό! Μόνο που στους Μηχανολόγους τους «αλωνίζουν» με την Fortran και έχει τα νεύρα του αν και του λέω να μάθει κι αυτήν. Έτσι κι αλλιώς στο να γράψεις κώδικα δεν μετρά η γλώσσα αυτή καθεαυτή όσο το ταλέντο σου να συνθέτεις «γλωσσικούς μηχανισμούς» οι οποίοι τρέχουν άψογα και δίχως προβλήματα.
Θα σε φέρω σε επαφή μαζί του και φιάξτε όσα objects τραβάει η όρεξή σας. Είναι C++ακιας αλλά τον έχω πιάσει να υποκλέπτει τις συναρτήσεις του SysUtils


Στίγμα Μαστροκαπετάνιου
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 04 August 2012 :  13:06:15  Show Profile
Απαντάω από την βόρεια Εύβοια, με το άρτι αποκτηθέν Aurdino έτοιμο για πειράματα.
quote:

Έχω τον γιο μου πρωτοετή στο ΕΜΠ σπασικλάκι κανονικό με δεκάρια κι έτσι.


Μάλλον ο γιος σου δεν είχε που να μοιάσει. Εύχομαι στον λεβέντη σου, κάθε επιτυχία. Ελπίζω μόνο να μην μελετάει μόνο για heat tanks αλλά να εκμεταλλευθεί στο έπακρο και το διαθέσιμο οικογενειακό think tank.

"Αιέν αριστεύειν και υπείροχον έμμεναι άλλων, μηδέ γένος πατέρων αισχυνέμεν».
Πάντα να είσαι πρώτος και ανώτερος από τους άλλους και να μην ντροπιάζεις τη γενιά των προγόνων. Ιλιάδα, Ζ 208

quote:

Μόνο που στους Μηχανολόγους τους «αλωνίζουν» με την Fortran και έχει τα νεύρα του αν και του λέω να μάθει κι αυτήν


http://www.computer.org/csdl/mags/co/2012/07/mco2012070008.html

quote:

Έτσι κι αλλιώς στο να γράψεις κώδικα δεν μετρά η γλώσσα αυτή καθεαυτή όσο το ταλέντο σου να συνθέτεις «γλωσσικούς μηχανισμούς» οι οποίοι τρέχουν άψογα και δίχως προβλήματα.


Η γλώσσα είναι το εργαλείο. Πρέπει να υποστηρίζει τις απαιτούμενες δυνατότητες. Η fortran, η C και η turbo pascal δεν υποστηρίζουν τους μηχανισμούς για να δηλώσεις κλάσεις και να υλοποιήσεις (instantiate) αντικείμενα.

Η σύνθεση, από απλούστερα ή δομικά αντικείμενα οδηγούν σε κατασκευές, γλωσσικές ή μη, που επιλύουν ένα πρόβλημα. (bottom-up).

Η ανάλυση, «σπάει» ένα σύνθετο αντικείμενο ή πρόβλημα, στα συστατικά του και το επιλύει επιλύοντας τα επιμέρους μικρότερα προβλήματα. (top-down)

quote:

Περιττό να προσθέσω ότι η ιδέα των objects είναι απόρροια των φοβερών records της Pascal. Το ηλιακό μου πρόγραμμα βασίσθηκε σε 3 τέτοια records που δόμησα όπως TSunData, THliakosStruc, TBoilerStruc. Για παράδειγμα η θερμοκρασία εξόδου από τον συλλέκτη είναι ιδιότητα του THliakosStruc ή η θερμοκρασία εισόδου στον εναλλάκτη του boiler είναι ιδιότητα του TBoilerStruc



Συμφωνώ, αλλά παρέλειψες τις ρουτίνες που χρησιμοποιούν τα records. Αυτό που προκύπτει και από τα δύο, το object δηλαδή, λειτουργεί σαν κάτι ενιαίο. Ενσωματώνει (encapsulates) δεδομένα και υπολογισμούς.

Επιστρέφω, στο πρόγραμμά σου γιατί αποτελεί, με δεδομένο το κοινό, το καλύτερο παράδειγμα αλλαγής από δομημένο σε αντικειμενοστρεφή προγραμματισμό.

To εγχειρίδιο των Ηλιακών συστημάτων http://www.monachos.gr/forum/topic.asp?TOPIC_ID=3832 αποτελεί μια πολύ καλή περιγραφή των φαινομένων. (stom ευχαριστώ)

Ο ήλιος ακτινοβολεί συγκεκριμένη ενέργεια ανάλογα με την περιοχή και την ώρα.



TSunEnergy = class

Public

// Ηλιακή ενέργεια στο συγκεκριμένο GeoPlatos, ημέρα και ώρα σε W/m2
    function Energy:Double; overload; 
// με μεταβλητή την ώρα σε W/m2
    function Energy(aTime:integer):Double; overload; 

    property Time:integer; // seconds since midnight
    property Month:integer;
    property Day:integer;
    property GeoPlatos:double;
    property SunPerc:double;// Συντελεστής ηλιοφάνειας
End;


Η ροή ενέργειας είναι το μόνο στοιχείο της ηλιακή ακτινοβολίας που ενδιαφέρει ένα panel. Τα υπόλοιπα στοιχεία είναι παράμετροι που χρειάζονται για τον υπολογισμό της ροής ενέργειας.

Η δήλωση της κλάσης γίνεται στο ανεξάρτητο uSunEnergy.pas


Ο ηλιακός συλλέκτης αποφορά την ενέργεια, και την μετατρέπει σε θερμική μέσω του ρευστού του κλειστού κυκλώματος.


TSolarPanel = class
public

    function Energy(EnergyIn):double; // συνολική για όλη την επιφάνεια kWh
    function Flow:double;    // ροή Lt/h

    property SolarAbsorbtion:double; // ποσοστό απορρόφησης ηλιακής ενέργειας
    property Slope :double; //κλίση ηλιακού
    property SouthDeviation:double; // Απόκλειση από τον Νότο
    property Surface:double; // εμβαδόν συλλεκτικής επιφάνειας

    property Efficiency:double; // απόδοση ηλιακού

    property GlycolePerc : double;
    property WaterInTemperature:double;  // θερμ. εισόδου κλειστού κυκλώματος
    property WaterOutTemperature:double; // θερμ. εξόδου κλειστού κυκλώματος

end;


H TSolarPanle.Energy δέχεται σαν παράμετρο την εισερχόμενη ενέργεια και υπολογίζει την αποδιδόμενη ενέργεια. Η ώρα της ημέρας είναι παράμετρος της TSunEnergy και όχι του panel.


Η δήλωση της κλάσης γίνεται στο ανεξάρτητο uSolarPanel.pas

Το boiler μέσω του εναλάκτη για τον ηλιακό ή του εναλάκτη για τον λέβητα ή την ηλεκτρική αντίσταση, ζεσταίνει το νερό που περιέχει


THeatSource = (hsSolarPanel, hsBurner, hsElectricity)

ΤBoiler = class

public

// κάνω την παραδοχή οτι μόνο μία πηγή ενέργειας τροφοδοτεί το boiler

    function Heat(inEnergy:double; from:THeatSource):double; // στιγμιαία ισχύς Kw

    function AccumulatedEnergy:double; // από το περιεχόμενο ζεστό νερό Kwh

    function SolarFraction:double; // ποσό ενέργειας απο ηλιο / ποσό ενέργειας κατανάλωσης

    property WaterVolume:double;
    property AverageDayConsuption:double; // lt 
    property WaterInTemperature:double;    // ανάλογα με την εποχή.
    property WaterOutTemperature:double;  // ανάλογα με την εποχή.

    property SolarHeatExchangeSurface :double;   // m2
    property BurnerHeatExchangeSurface :double; // m2

end;


To boiler συσσωρεύει θερμότητα και αποδίδει λίτρα ΖΝΧ σε συγκεκριμένη θερμοκρασία στην μονάδα του χρόνου.

Η δήλωση της κλάσης γίνεται στο ανεξάρτητο uBoiler.pas


Όλα τα objects σχεδιάστηκαν για να δίνουν στιγμιαίες τιμές. Για να παράξουμε τα διαγράμματα ακτινοβολίας θα πρέπει να καλέσουμε την TSunEnergy.Energy(aTime) με όρισμα χρόνους που διαφέρουν μεταξύ τους κατά 15 min.

H κλάση TCalculator γνωρίζει όλα τα παραπάνω αντικείμενα (TSunEnergy, TSolarPanel, ΤBoiler) βλέπει (uses) τα uSunEnergy.pas, uSolarPanel.pas, uBoiler.pas και υπολογίζει

ένα σύνολο τιμών,

για μία ημερομηνία aDate,
από ώρα FromTime έως ToTime με βήμα step λεπτά
για τον συλλέκτη aSolarPanel
και το boiler aBoiler.


TCalculator = class

    procedure Calc(aDate:TDate; // για συγκεκριμένη ημερομηνία
                   FromTime, ToTime:TTime; step: integer;
                   aSolarPanel:TSolarPanel;
                   aBoiler:TBoiler);
    property CalcCount:integer;
    property Time[index:integer]:TTime; //
    property SunHeight[index:integer]:degrees;
    property SunAzimou8io [index:integer]:degrees;
    property BoilerTemp [index:integer]:double;
    property CollectorTempIn [index:integer]:double;
    property CollectorTempOut [index:integer]:double;
...
end;


Στο επόμενο post θα αναφερθώ στα threads, την βασική οντότητα που εκτελεί το CPU.

Go to Top of Page

stom
Senior Member

Greece
1665 Posts

Posted - 05 August 2012 :  10:15:50  Show Profile
Ποσο βορεια δλδ?
Χτες ημουν στο πηλι..

Arduino στις διακοπες. Τι να πω..
Εγω παλι κουβαλαω κατευθυντικες yagi κεραιες για τους 2,4....

Συνηθως ολο και καποιο wifi βρισκεται και βολευομαστε ολοι.

Στηνεται ενα laptop σε repeater mode και βολευομαστε ολοι.
Τηλεφωνα, laptop, psp και δεν συμμαζευεται..

Και για να μην παιδευομαστε...
http://seventhgate.codeplex.com/



ΥΓ sorry για το ασχετο post.... αλλα το μονο σχολιο ειναι
πως θα παντρεψεις το arduino και την object pascal?



Burn baby, burn
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 05 August 2012 :  12:14:14  Show Profile
quote:
Ποσο βορεια δλδ?

Στο Νέο Πύργο, δίπλα στους Ωρεούς
πως θα παντρεψεις το arduino και την object pascal?


Εχω τελειώσει ένα project με Delphi και επικοινωνία με modbus over TCP/Ip με ένα slave PLC S1200. Μου ζητήθηκε να προτείνω εναλακτικό h/w.

Eυτυχώς υπάρχει η γλυκιά C. Δεν μπορείς να πας πιο κοντά στην assembly με άλλη γλώσσα. Εγραφα με Zortech C compiler το 1989. Μετά βγήκε η καταπληκτική Borland C/C++ (386 instructions, code optimizations).
Και μετά ξύπνησε ο M$oft και τους έκλεισε όλους.
Go to Top of Page

stom
Senior Member

Greece
1665 Posts

Posted - 05 August 2012 :  13:37:05  Show Profile
Απο οτι βλεπω, υπαρχει ετοιμο modbus slave για arduinia..
Αμα ο κοσμος εχει ορεξη και χρονο..
Δεν το ψαξα, αλλα ισως υπαρχει και σε master.
Αλλα αμα εχεις το μαχαιρι και το καρπουζι, υλοποιεις οτι θες.

Εχεις βρει κανα τροπο να μεταφερεις το αποτελεσμα της object pascal
σε περιβαλλον εκτος windows? Χωρις τυχον παραθυρικο..

Τα windows για αυτοματισμο χαμηλου επιπεδου ειναι καπως δυστροπα...

Προβλεπω οτι συντομα η καλυτερη πλατφορμα αυτοματισμου θα ειναι android based...


Burn baby, burn
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 05 August 2012 :  22:44:17  Show Profile
quote:
Τα windows για αυτοματισμο χαμηλου επιπεδου ειναι καπως δυστροπα...

Συμφωνώ, τα winda κατάφεραν να γίνουν λειτουργικό στην έκδοση ΝΤ. Ακόμα και τότε όμως η εκτύπωση ενός μεγάλου αρχείου έπαιρνε όλη την μνήμη και γονάτιζε τις υπόλοιπες εφαρμογές.
Ακόμη και σήμερα ένα reboot κάθε τόσο τα "στρώνουν".

Αναφορικά με την εμφάνιση (user interface) νομίζω ο Jobs στην ομιλία του στο Standford τα λέει όλα.

quote:
Εχεις βρει κανα τροπο να μεταφερεις το αποτελεσμα της object pascal σε περιβαλλον εκτος windows? Χωρις τυχον παραθυρικο..


Η συλλογή δεδομένων γίνεται σε windows service, χωρίς οθόνη κτλ.
Ενημερώνεις τα αποτελέσματα στην μνήμη όταν παράγονται και σε αρχείο και
1. τα «φωνάζεις» περιοδικά στο καλώδιο με UDP data έως 1,5k.
http://www.cyberciti.biz/tips/linux-netconsole-log-management-tutorial.html

2. μέσω κοινής (shared) μνήμης όπως η Win32 outputdebugstring http://www.codeproject.com/Articles/23776/Mechanism-of-OutputDebugString
δες τον δέκτη (debugview)
http://technet.microsoft.com/en-us/sysinternals/bb842059.aspx

3. γίνεσαι εκδότης (publisher) και δέχεσαι TCP/IP συνδέσεις από συνδρομητές και τους διαθέτεις τα δεδομένα.

4. Γράφεις τα δεδομένα σε μία database και τα διαβάζουν οι ενδιαφερόμενοι.

quote:

Προβλεπω οτι συντομα η καλυτερη πλατφορμα αυτοματισμου θα ειναι android based...

Mobile + core linux + Open Source + Java ή FPC + Java GUI API
Αν προσθέσεις και optoisolated I/O και μια αξιοπρεπή τροφοδοσία.
και νέα APIs για να μάθουμε..

Θα αλώσει και το latop/desktop περιβάλλον ? Δέχεσαι τα αρχεία σου να είναι στο συννεφάκι και να τα προσπελαύνεις από παντού ?
Go to Top of Page

stom
Senior Member

Greece
1665 Posts

Posted - 06 August 2012 :  09:59:29  Show Profile
Το αν δεχομαι τα data μου στο cloud λιγη σημασια εχει.
Η μεγαλη μαζα παραμενει clueless..
Ασε που τα εχει χασει 5-6 φορες μιας και η λεξη backup ειναι αγνωστη.
Εχω εφαρμοσει κοινοχρηστα στο cloud, και ολοι ειναι happy...
Ολα τα δελτια συντηρησης βρισκονται εκει.
Οποιος θελει, μπαινει και τα βλεπει.
Οπως και εγχειριδια, σχεδια κλπ.


Βεβαια το δικο μου λεβητοστασιο εχει din rail ethernet switch και συντομα θα εχει και wifi access point οποτε δεν ειμαι τυπικη περιπτωση.
(αμα εισαι κατω και θες να δεις μια λεπτομερια στο manual του καυστηρα πχ, ανοιγεις το κινητο, και κατεβαζεις το pdf του κατασκευαστη από το wifi...) Αλλιως πρεπει να ανεβεις επανω, να τυπωσεις και να ξανακατεβεις.
Μονο μειονεκτημα οτι θελει καθαρα χερια.)


Το μοντελο που περιγραφεις ειναι λιγο μεγαλο.
Βεβαια οποιος το χρειαζεται, το χρειαζεται λογω μεγεθους...
Σε μικροτερες εγκαταστασεις, οπως λεβητοστασια, εξυπνα σπιτια κλπ
μαλλον χρειαζομαστε κατι λιγοτερο.
Τυπικα ενα αξιοπιστο multitasking os, ενα καλο tcp/ip stack
και ενα ευκολο web server based διαχειριστικο.
Ακομα και για databases, για απλα πραγματα, οι textuριες δουλευουν μια χαρα.

Το laptop περιβαλλον εχει αλλωθει.
Ξεκινησαμε με τα netbook, πηγαμε στα tablet και τωρα που πεφτει το κοστος θα γινουν mainstream.
Το δειχνουν και οι πωλησεις εξαλλου.
Και αυτη η προσεγγιση ταιριαζει γαντι στη λογικη του cloud.
Η μηπως το cloud ειναι που τα κανει τοσο πετυχημενα?
Μαλλον και τα δυο.

Το linux εχει αποτυχει ως desktop. Ο λογος ειναι απλος.
Πανσπερμια διανομων, και περιστασιακη υποστηριξη απο τους κατασκευαστες hardware. Το τι δουλευει απο περιφερειακα ειναι περισσοτερο θεμα τυχης. Αν δεν, το μονο που μενει ειναι να γραψεις ΜΟΝΟΣ σου τους drivers. Καληνυχτα linux desktop

Το computing για τις μαζες θα ερθει μεσω ενος κινητου πηγμενου στα αναβολικά.
Ξεχειλωνει η οθονη, μεγαλωνει η flash, αυξανεται η μνήμη, ανεβαινει η αναλυση, και καταληγουμε σε ενα υπολογιστη on steroids...
Ομως εντωμεταξυ η συμβατοτητα με τα windows εχει περιορισθει στα αρχεια του office...

Η Microsoft θα ειναι η νεα Novell. Για οποιον την θυμαται.

Η Apple προσπαθει με νυχια και δοντια να μας πεισει οτι ολη η ιδεα του cloud computing ειναι (κατ επεκταση)δικια της. Προσπαθει να πνιξει το android για να πουλαει μονο τα απαραδεκτα υπερτιμημενα προϊόντα της.

Η Microsoft το καταλαβε αυτο αρκετα νωρις και αφησε τα προιόντα της να πειρατευτουν μαζικα. Το πανηγυρι κρατησε πανω απο 25 χρονια.
Και τωρα χανει γιατι καποιος αλλος εφτιαξε κατι αλλο και το δινει απο την αρχη και μαζικα δωρεαν.

Η Apple φαινομενικα λυσαει, αλλα ολα ειναι θεμα καποιων χρηματων.
Ουτως η αλλως εχει ήδη βγαλει αρκετά.

Ο αυτοματισμος παντα θα ειναι συντηρητικος στην υιοθετηση νεων πραγματων σε αυτα τα επιπεδα.
Ομως θα οδηγηθει εκει κυριως λογω κοστους..

Τελικα αυτο που μενει ειναι οι βασικες αρχες και η μεθοδολογια προσεγγισης. Ολα τα αλλα μετα απο λιγο καιρο εξελλισονται, οποτε αν δεν εισαι στις επαλξεις καταληγεις να ψαχνεσαι.

Απο την αλλη, οτι δεν εξελισσεται πεθαινει...

Νομιζω οτι ειναι ωρα για μια βουτια ομως.

Burn baby, burn
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 06 August 2012 :  18:49:01  Show Profile
quote:
Απο την αλλη, οτι δεν εξελισσεται πεθαινει...

ποσο αληθινό...



Διεργασίες (Processes) – Νήματα (Threads)

Οι πρώτοι μικρουπολογιστές είχαν μια Κεντρική Μονάδα Επεξεργασίας (CPU) με ικανότητα χειρισμού δεδομένων μήκους 8 bits και υποστήριζαν εξωτερική μνήμη έως 64ΚBytes. Εσωτερικά είχαν καταχωρητές (registers) στους οποίους «φόρτωναν» τα δεδομένα από την μνήμη και τον δείκτη προγράμματος (Instruction Pointer) που έδειχνε την επόμενη προς εκτέλεση εντολή. Οι εντολές ήταν ανάκληση/αποθήκευση δεδομένων στην μνήμη και αριθμητικές και λογικές πράξεις ανάμεσα σε δυαδικούς αριθμούς. Η πρόσθεση 2 αριθμών 16 bits (0..64535) γινόταν με πρόγραμμα.
Υπήρχε η δυνατότητα άμεσης εξυπηρέτησης εξωτερικών διακοπών (interrupts).

Τα ψηφία που χρησιμοποιούμε στο δεκαδικό σύστημα είναι 10 από 0..9. Μόλις ξεπεράσουμε το 9 προσθέτουμε ένα άσσο στο αριστερό ψηφίο (στο 0 αν δεν υπάρχει). Στο δεκαεξαδικό τα ψηφία είναι 16 τα 0..9,A,B,C,D,E,F στο οκταδικό 8 τα 0..7 και στο δυαδικό μόλις 2 τα 0..1 (άγει / δεν άγει)

Εξου και η υπογραφή του Dipoli.
There are only 10 types of people in the world: Those who understand binary, and those who don't.

Είναι προφανής η σχέση του αριθμού των δακτύλων μας με το αριθμητικό σύστημα που χρησιμοποιούμε.

Με την εκκίνηση της η ΚΜΕ έτρεχε το πρόγραμμα που υπήρχε γραμμένο στο BIOS.
Αυτό το πρόγραμμα εκκίνησης, με την σειρά του έψαχνε για συσκευές (κασετόφωνο, floppy disk, hard disk, κάρτα δικτύου) από τις οποίες διάβαζε το υπόλοιπο πρόγραμμα που θα εκτελούσε συνήθως ένα πρωτόγονο λειτουργικό σύστημα.

Η μνήμη RAM ήταν κοινή για προγράμματα και δεδομένα και μόνο ένα πρόγραμμα μπορούσε να εκτελεσθεί κάθε φορά. Η οθόνη έδειχνε μόνο χαρακτήρες.

Οι σημερινοί μ/υ, εκτελούν μια αντίστοιχη διαδικασία εκκίνησης και το πρόγραμμα που φορτώνουν και αρχικά εκτελούν λέγεται λειτουργικό σύστημα.
Το λειτουργικό σύστημα (windows, linux) διαχειρίζεται τους χρήστες με τα δικαιώματά τους, την χρήση της μνήμης και των περιφερειακών συσκευών και εμφανίζουν στην οθόνη τους ένα περιβάλλον εργασίας που προσομοιάζει την επιφάνεια του γραφείου μας.

Το λειτουργικό σύστημα (λ/σ) διαιρεί το χρόνο επεξεργασίας της ΚΜΕ σε φέτες (slices) και επιτρέπει την εκτέλεση πολλών εφαρμογές παράλληλα κατανέμοντας σε κάθε μία ίσο χρόνο εκτέλεσης.
Το λ/σ δημιουργεί για κάθε εφαρμογή (processes) ένα ιδεατό περιβάλλον, απομονωμένο από τις άλλες εφαρμογές ή το ίδιο το λ/σ, επιτρέπει την πρόσβαση στις περιφερειακές συσκευές (κάθε εφαρμογή νομίζει ότι της ανήκει το ένα και μοναδικό mouse) και επιτρέπει την κοινή χρήση του κώδικα των ήδη φορτωμένων dlls.

Κάθε εφαρμογή ξεκινάει και συνήθως κατευθύνεται από τον χρήστη βάσει των επιλογών του menu. Δηλαδή κάθεται και περιμένει το χρήστη να κάνει μία επιλογή ή να εισάγει μία τιμή. Είναι πολλές φορές αναγκαίο να εκτελεσθούν και άλλες λειτουργίες παράλληλα (πχ να δέχεται απομακρυσμένες εντολές μέσω του δικτύου). Ένα νέο νήμα (thread) εκτέλεσης προγράμματος, μέσα στα όρια της εφαρμογής αλλά χωρίς την επιβάρυνση την δημιουργίας νέου process.

Κάθε εφαρμογή έχει ένα κύριο νήμα (main thread) εκτέλεσης που χειρίζεται την εμφάνιση της εφαρμογής (ενημέρωση παραθύρων κτλ). Αν, πιέζοντας ένα button, προκαλούμε ένα χρονοβόρο υπολογισμό τότε σε αυτό το διάστημα η οθόνη θα «παγώσει». Μπορούμε να δημιουργήσουμε ένα νέο νήμα που παράλληλα θα υπολογίσει το ζητούμενο χωρίς να μπλοκάρει την κυρίως εφαρμογή.
Η ΚΜΕ εκτελεί κάθε φορά ένα thread. Με την έλευση των πολλαπλών πυρήνων ή των πολλαπλών ΚΜΕ το λ/σ μπορεί να προγραμματίσει την παράλληλη εκτέλεση των threads σε όλους του διαθέσιμους πυρήνες.

Συγχρονισμός.

Έστω ότι έχουμε δύο threads που πρέπει, το καθένα, να αυξήσει κατά 1 την τιμή μιας μεταβλητής. Η διαδικασία σε ψευτο-γλώσσα μηχανής είναι

move variable, accum, 
Add  accum,1
move accum, variable

Αν η πρώτη διαδικασία ξεκινήσει τότε η δεύτερη θα πρέπει να περιμένει έως ότου ενημερωθεί η μνήμη με την νέα τιμή. Η ΚΜΕ διακόπτει την εκτέλεση του 1ου thread μετά την πρώτη εντολή και προγραμματίζει για εκτέλεση το 2ο thread. Έτσι αντί να έχουμε τελική τιμή 2 θα έχουμε τιμή 1.

Χρειαζόμαστε σηματοδότες στους οποίους περιμένουμε στο κόκκινο και ξεκινάμε στο πράσινο.

Lock                 // ο σηματοδότης γίνεται κόκκινος
Load accum, variable // φώρτωσε την μεταβλητη στο καταχωρητή
Inc accum,1          //  αυξησε την τιμή του καταχωρητή κατά 1 
Save accum, variable // αποθήκευσε τον καταχωρητή στη μεταβλητή
Unlock               // ο σηματοδότης γίνεται πράσινος

Η χρήση του σηματοδότη αναγκάζει όλες τις διαδικασίες να μπουν στην σειρά και εξασφαλίζει την σωστή πρόσβαση σε κοινά δεδομένα.

Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 12 August 2012 :  18:32:16  Show Profile
Threads συνέχεια

Για να ορίσουμε ένα νέο TMyThread κληρονομούμε την βασική κλάση TThread και αντικαθιστούμε (override) την μέθοδο execute.

Ενσωματώνουμε στην νέα κλάση αντικείμενα ή μεταβλητές που απαιτούνται.
Το thread εκτελεί οτι ορίζει η ρουτίνα Execute. Όταν αυτή τελειώσει τότε τελειώνει και το thread.

Δημιουργούμε ένα TMyThread με την χρήση του constructor TMyThread. Create(CreateSuspended: Boolean). Συνήθως το δημιουργούμε ανενεργό TThread.Create(true), αρχικοποιούμε όσες μεταβλητές του ΤThread απαιτείται και στην συνέχεια καλούμε την μέθοδο Resume για να το ενεργοποιήσουμε.

TFileCopy = class(TThread)
   private
    FsrcFNames,
    FdstFnames:TStrings;
   protected
    procedure Execute; override;
   public
    constructor CopyFiles(const srcFNames,dstFnames:TStrings);
    procedure   Stop;
  end;

…

procedure TFileCopy.Execute;
...
begin
...
  while not Terminated do
   begin

// πως χρησιμοποιούμε το try/finally με πολλά objects

    fsrc:=nil;
    fdst:=nil;
    try
…
// Ελέγχουμε πάντα τα exceptions γιατί διαφορετικά 
// διακόπτεται η εκτέλεση του thread

     try

// επικοινωνία με το κύρια φόρμα της εφαρμογής
      PostMessage(UIHandle, COPY_EVENT, 1, fsrc.Size); 

     Except

// επικοινωνία με το κύρια φόρμα της εφαρμογής με strings
      debug(Exception(ExceptObject).Message);
      PostMessage(UIHandle, COPY_EVENT, 0, 0);

     end;

    finally
     fdst.free;
     fsrc.Free;
    end;

   end;

Διακοπή εκτέλεσης του thread

procedure TFileCopy.Stop;
 begin
  Terminate;
  WaitFor;
 end;


Επικοινωνία με το κύρια φόρμα της εφαρμογής με strings.

TMainFrm = class(TForm)
   …
    procedure LogEvent(var Msg: TMessage); message LOG_EVENT;
   …
   end;

procedure TMainFrm.LogEvent(var Msg: TMessage);
 begin
  Log(pchar(msg.wParam));
  StrDispose(pchar(msg.wParam));
 end;

procedure TMainFrm.FormCreate(Sender: TObject);
begin
....
 UIHandle:=Handle;
end;

procedure Debug(const msg:string);
 var p:pchar;
 begin
  if UIHandle=INVALID_HANDLE_VALUE then
   outputdebugstring(pchar(msg))
  else begin
   p:=strNew(pchar(msg));
   PostMessage(UIHandle,LOG_EVENT,integer(p),0)
  end;
 end;


Προσοχή στα strings ή σε reference counted μεταβλητές που είναι μέλη ενός record. Δεν μπορούμε να αρχικοποιήσουμε το record με την Fillchar. (βλέπε procedure Finalize)

Στο http://www.filefactory.com/file/6zopl4flpvdv/n/Example01.rar θα βρείτε το source & exe για το παραπάνω παράδειγμα.
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 21 August 2012 :  04:15:12  Show Profile
Συνέχεια στα threads και εισαγωγή στα Sockets

Κάτι οι βουτιές στο τσουκαϊτι, κάτι τα τσίπουρα, κάτι η πολυπλοκότητα του θέματος καθυστέρησαν την πολυαναμενόμενη συνέχεια. Αλλά φευ, συνεχίζω ακάθεκτος.

Η πλήρης εκμετάλλευση των δυνατοτήτων των νέων Η/Υ, συνεπάγεται χρήση όλων των πυρήνων και τη δημιουργία εφαρμογών που εκτελούν τα υπολογιστικά τους καθήκοντα παράλληλα, μοιράζοντας τα σε μια ομάδα από διαθέσιμους εργάτες νήματα (working Threads). Το λειτουργικό σύστημα, αναθέτει την εκτέλεση των νημάτων στους υπάρχοντες πυρήνες (cores).

Θα σχεδιάσουμε μία εφαρμογή που θα μπορεί να «κατεβάσει» ένα ολόκληρο ιστότοπο (www.monachos.gr) σε τοπικό δίσκο. Θα ξεκινά με την πρώτη σελίδα (/forum/) και αφού την κατεβάσει, θα ψάξει για τις υπάρχουσες αναφορές της σε άλλες σελίδες (στο ίδιο ή άλλο ιστότοπο). Η διαδικασία θα είναι αναδρομική. Η ανάγνωση μίας σελίδας από ένα ιστότοπο (βασική λειτουργία του περιηγητή) απαιτεί χρήση Sockets και του πρωτοκόλλου HTTP 1.0/1.1

Sockets (υποδοχή)

Socket είναι μια υποδοχή που επιτρέπει την επικοινωνία ανάμεσα σε 2 Η/Υ (server & client) για την ανταλλαγή δεδομένων μέσω της δικτυακής υποδομής.

Στόχος είναι η δημιουργία, ανάμεσα σε δύο άκρα, ένα ιδεατού κυκλώματος ψηφιακής επικοινωνίας.

Το άκρο του πελάτη εκκινεί την σύνδεση, χρησιμοποιώντας μία υποδοχή πελάτη (client socket) την διεύθυνση και την θύρα του εξυπηρετητή. Ο εξυπηρετητής, μέσω μίας υποδοχής αναμονής (listening socket) υποδέχεται συνδέσεις. Κάθε αίτηση σύνδεσης, δημιουργεί το 2η άκρο, μία νέα υποδοχή εξυπηρετητή (server socket) αποκλειστικά για αυτή τη σύνδεση και συνήθως ένα νήμα που θα χειρισθεί την επικοινωνία των 2 άκρων.

http://www.inetdaemon.com/tutorials/internet/tcp/3-way_handshake.shtml

Εφαρμογή SiteGrab

Αν υποθέσουμε ότι έχουμε μια ομάδα από εργάτες νήματα και μία λίστα διαφόρων εργασιών. Ένας επιστάτης διατηρεί την λίστα εργασιών ενημερωμένη, ελέγχει την διαθεσιμότητα κάθε εργάτη νήμα και αναθέτει εκκρεμείς εργασίες σε ελεύθερους εργάτες νήματα έως ότου όλοι οι εργάτες νήματα είναι απασχολημένοι ή έχει αναθέσει όλες τις εργασίες προς εκτέλεση. Μόλις ο επιστάτης τελειώσει με την λίστα εργασιών «κοιμάται» (δεν καταναλώνει πόρους του Η/Υ) έως ότου ανατεθεί νέα εργασία. Ίσως, από κοινωνιολογική άποψη, μια σύγκριση με το αντίστοιχο ανθρώπινο μοντέλο εργασίας να μας οδηγούσε σε ενδιαφέροντα συμπεράσματα.

Η εφαρμογή δημιουργεί ένα object εργασία και το αναθέτει στον επιστάτη για να το υλοποιήσει.

Κλάση Εργασία
Η κλάση εργασία (TBasicWorkObject) περιλαμβάνει όλα τα απαραίτητα δεδομένα και μία μέθοδο εκτέλεσης της.

Ορίζεται σαν

TBasicWorkObject=class
   public
    procedure doWork; virtual; abstract;
   end;


Η πραγματική εργασία TGrabUrl κληρονομεί απο την TBasicWorkObject και γνωρίζει να «κατεβάζει» μία σελίδα από ένα ιστότοπο.

TGrabUrl=class(TBasicWorkObject)
   private
    FClientSocket: TClientSocket;
..
   public
    procedure   doWork; override;
    constructor Create(const aSite,aUrl:string;aDepth:integer); overload;
..
    property    Stream:TMemoryStream read FStream;
    property    Depth:integer read FDepth;
  end;


Η TGrabUrl.doWork κατεβάζει την σελίδα και ενημερώνει την κύρια φόρμα

postmessage(UIHandle,WORK_DONE,integer(self),0);

Εκεί γίνεται η επεξεργασία και η αποθήκευση της σελίδας.

Κάθε ιστότοπος έχει δενδρική δομή και κάθε επίπεδο χαρακτηρίζεται απο το βάθος του.

Κλάση Επιστάτης

Η κλάση επιστάτης, διατηρεί την λίστα εργασιών για κάθε σελίδα που πρέπει να κατεβάσει και δημιουργεί και ελέγχει τα νήματα εργάτες.
Ξυπνάει περιοδικά, και αν υπάρχει εργασία και διαθέσιμο νήμα εργάτης την προγραμματίζει προς εκτέλεση.

TThreadsMngr=class(TThread)
   private
    FPeriod:integer;
    FThreads,
    FWorkQ :TObjectList;
   ...
   public
    constructor Create(WorkThreadClass:TWorkThreadClass;
                       DefThreadCount:integer=10);
    ...
    procedure   QueueWork(work:TBasicWorkObject);
  end;


Εργάτες νήματα

Κάθε εργάτης νήμα «κοιμάται» και περιμένει 2 σηματοδότες (events) να ενεργοποιηθούν για να δουλέψει ή να τερματίσει. Εκτελεί την εργασία του, καλώντας την μέθοδο TBasicWorkObject.doWork και μόλις τελειώσει ξανακοιμάται.

TWorkThread=class(TThread)
   private
    FWorkObj   : TBasicWorkObject;
    ...
   public
    constructor Create(aId:integer;aHandle:THandle);
    procedure  Work(aWorkObj:TBasicWorkObject);
    ...
   end;

procedure TWorkThread.Work(aWorkObj:TBasicWorkObject);
 begin
  FWorkObj:=aWorkObj;
  FWorkEvent.SetEvent;
  if Suspended then Resume; // FWorkEvent will signal imediatelly
 end;

procedure TWorkThread.Execute;
begin
  H[0] := FStopEvent.Handle;
  H[1] := FWorkEvent.Handle;

  while not Terminated do
   case WaitForMultipleObjects(2, @H, False, INFINITE) of
    WAIT_OBJECT_0     : Terminate; // StopEvent Signaled
    WAIT_OBJECT_0 + 1 : begin // WorkEvent Signaled
                         try
                          FWorkObj.doWork;
                         except
                         end;
                         FWorkEvent.ResetEvent; // Lower WorkEvent
                        end;
   end

 end;

Στο παράδειγμα υλοποίησα την ανάγνωση της πρώτης σελίδας /forum/ από τον ιστότοπο (www.monachos.gr ) και ανεύρεση όλων των αναφορών σε σελίδες (πχ http://www.monachos.gr/forum/topic.asp? TOPIC_ID=3931) 1ου επιπέδου που περιέχει .

Η αρχική σελίδα αποθηκεύεται σαν c:\temp\forum\default.htm

Μπορείτε να κατεβάσετε την εφαρμογή και το source από το example02.rar
http://www.filefactory.com/file/1pjnx7y8tu2p/n/Example02_rar


There always be one more bug.
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 25 August 2012 :  21:09:01  Show Profile
TCP Sockets και modbus

H Borland, συγνώμη Embarcadero, σταμάτησε σιωπηλά την υποστήριξη στα socket components και στην θέση τους πρότεινε τα Open Source Indy components.

Αρχικά η επικοινωνία ενός client με ένα server υλοποιήθηκε σαν σύγχρονη. Ο πελάτης περίμενε (blocking) έως ότου πάρει απάντηση και δεν μπορούσε να κάνει κάτι άλλο. Για να εξυπηρετηθεί η επικοινωνία περισσοτέρων πελατών σε κάθε σύνδεση με το listening socket επικράτησε η χρήση ενός νήματος αποκλειστικά για την συγκεκριμένη επικοινωνία. Τα Indy υποστήριξαν το ασύγχρονο μοντέλο με το οποίο αποστέλλεται ειδοποίηση όταν η απάντηση είναι διαθέσιμη.

Η σύγχρονη (blocking) επικοινωνία (σειριακή ή δικτυακή) είναι πιο απλή και πιο εύκολη στη υλοποίηση.

Άλλες βιβλιοθήκες με υποστήριξη TCP/IP είναι
http://synapse.ararat.cz/doku.php
http://lnet.wordpress.com/

Κλάσεις για sockets


              TCustomWinSocket (abstract)
          |              |                 |                
TClientWinSocket  TServerClientWinSocket TServerWinSocket


Υποδοχή πελάτη (client socket) : TClientWinSocket
Υποδοχή αναμονής (listening socket) : TServerWinSocket
Υποδοχή εξυπηρετητή (server socket): TServerClientWinSocket

Components (μπορούμε να ορίσουμε τιμές στις ιδιότητες τους μέσω του Object inspector). Χρησιμοποιούν το TCustomWinSocket


           TAbstractSocket (abstract)
                    |
              TCustomSocket (abstract)
                |         |
     TClientSocket   TCustomServerSocket (abstract)
                             |
                        TServerSocket       

Χρησιμοποιώ την κλάση TWinSocketStream που μου παρουσιάζει την διαδικασία εγγραφή ή ανάγνωσης από socket σαν σε αρχείο.

ModBus

Είναι ένα πρωτόκολλο επικοινωνίας για βιομηχανικές συσκευές ελέγχου διαδικασιών και των συσκευών επιτήρησης τους. Επιτρέπει την μετάδοση της κατάστασης ψηφιακών/αναλογικών εισόδων (από τις συσκευές που τις συλλέγουν) και τον καθορισμό παραμέτρων αναφοράς από τους επιτηρητές.

http://www.prosoft-technology.com/kb/assets/intro_modbustcp.pdf
http://www.modbus.org/tech.php

Η επικοινωνία απαιτεί 2 συσκευές (master-slave). Η master συσκευή εκκινεί την επικοινωνία και ρωτάει ή δίνει εντολές στον slave. Οι slave συσκευές απαντούν στα ερωτήματα που τους απευθύνει ο master.

Βασισμένη στα Indy componets, υπάρχει η ελεύθερη ModBusTCP βιβλιοθήκη (http://sourceforge.net/projects/delphimodbus/) που υλοποιούν το modbus σε TCP/IP.

Μια απλή εφαρμογή είναι η επιτήρηση της κατάστασης ενός slave PLC που με την σειρά του ελέγχει μια διαδικασία. Θα χρειασθούμε ένα modbus master που θα «μιλήσει» με function codes 3 με το PLC και θα διαβάσει ή θα γράψει σε μνήμες που αντιστοιχούν σε εισόδους (ψηφιακές ή αναλογικές) ή εξόδους.

Για να υλοποιήσουμε μία master modbus εφαρμογή απαιτείται ένα απλό client socket.
Μία slave modbus εφαρμογή είναι στην ουσία ένας πολυνηματικός tcp/ip server που ακούει στην θύρα 502 και αποδέχεται μηνύματα στην παρακάτω μορφή.


|Modbus Application Protocol (MBAP) Header | Protocol Data Unit (PDU)|
|------------------------------------------+-------------------------|
|Transaction |Protocol  | Length  | Unit   | Function   |   Data     |
|Identifier  |Identifier| Field   | ID     | Code       |            |
|--------------------------------------------------------------------|
| (2 bytes)  |(2 bytes) |(2 bytes)|(1 byte)|(1 byte)    | Μεταβλητό  |
|------------------------------------------+-------------------------|


Μπορείτε να κατεβάσετε την εφαρμογή και το source από το ModbusMaster.rar

http://www.filefactory.com/file/6skbkpq7x7rb/n/ModbusMaster_rar

Και για δοκιμές της επικοινωνίας υπάρχει ο Modbus PLC Simulator
http://www.plcsimulator.org/



H τιμή $AA στην θέση 4001 +0 απεικονίζεται στα leds M100.0 .. M100.7
H float32 τιμή στην θέση 4001 +1 & 4001 +2 εμφανίζεται σαν θερμοκρασία.
Τα 3 πρώτα bits στην θέση 4001+3 απεικονίζεται στα leds M106.0 .. M106.2

Οι έξοδοι προκαλούν αλλαγές στο high byte στην θέση 4001 +0.

There’s always one more bug.
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 28 August 2012 :  22:38:45  Show Profile
Modbus & big endian vs little endian

Έστω ότι έχουμε ένα 16 bit αριθμό 0x1234.

Πως αποθηκεύεται στην μνήμη του υπολογιστή ?.

Αν υποθέσουμε ότι η πρώτη θέση μνήμης έχει διεύθυνση 0, η επόμενη 1 κοκ τότε σε KME (CPU) της intel το νούμερο θα αποθηκευτεί σαν 34 12 (little endian) ενώ σε KME της Motorola (o εκπληκτικός 68000) το νούμερο θα αποθηκευτεί σαν 12 34 (big endian).

Προσομοιωτής. Εκπληκτικό εργαλείο, αρκεί να τηρείς τις αναλογίες με το πραγματικό κόσμο.

Το modbus χρησιμοποιεί big endian διάταξη των αριθμών στα διάφορά μυνήματά του.

Κατά συνέπεια αν θέλουμε να ελένξουμε στο πρόγραμμά μας την ύπαρξη alarm (bit 0) με μάσκα 0x0001 η τιμή που πρέπει να εισάγουμε στο simulator είναι 0100 και ΟΧΙ 0001.

Μικρές λεπτομέρειες ...
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 07 September 2012 :  18:04:37  Show Profile
TCP/IP Server
Θα υλοποιήσω ένα απλό TCP/IP πολυνηματικό διακομιστή (Server) χρησιμοποιώντας τα TServerSocket & TClientSocket components.
(Απαιτείται να εγκατασταθεί το package /bin/dclsockets70.bpl)
Στο http://delphi.about.com/od/networking/l/aa112602a.htm θα βρείτε περισσότερες πληροφορίες για την χρήση των συγκεκριμένων components.
Επίσης στο http://www.overbyte.be/frame_index.html?redirTo=/products/ics.html θα βρείτε μια διαφορετική υλοποίηση από Indy και Delphi socket components.

Για πελάτες (clients) θα χρησιμοποιήσουμε

1. Ένα απλό browser που θα «χτυπάει» μία http διεύθυνση
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]

και στην περίπτωσή μας http://127.0.0.1:1024

H παραπάνω HTTP κλήση εμφανίζεται στον Server σαν το παρακάτω κείμενο
GET / HTTP/1.1
Host: 127.0.0.1:1024
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:15.0) Gecko/20100101 Firefox/15.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,el;q=0.8,el-gr;q=0.5,en-us;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive


Ο Server απαντά

HTTP/1.1 200 OK
Content-Length: 11
Hello World
Connection: close
[empty line]



2. Το telnet στο οποίο θα επιστρέφεται ότι στέλνουμε (echo).
Start/Run Cmd
Στο Dos box πληκτρολογούμε telnet 127.0.0.1 1024
Πληκτρολογούμε «Hello» και το αποστέλλουμε στον Server με Enter
Client  ---------- initate connection -----------> Server
...
Client  <---------------command------------------- Server
Client  ----------------response-----------------> Server
...
Client  <---------------command------------------- Server
Client  ----------------response-----------------> Server


Πρέπει να υλοποιηθεί το RFC 854, και δεν μπορεί να συνυπάρξει με το modbus ή το Web server


3. To modbus client.
Θα πρέπει να επιλέξουμε για θύρα που ακούει ο Server την 502.

|Modbus Application Protocol (MBAP) Header | Protocol Data Unit (PDU)|
|------------------------------------------+-------------------------|
|Transaction |Protocol  | Length  | Unit   | Function   |   Data     |
|Identifier  |Identifier| Field   | ID     | Code       |            |
|--------------------------------------------------------------------|
| (2 bytes)  |(2 bytes) |(2 bytes)|(1 byte)|(1 byte)    | Μεταβλητό  |
|------------------------------------------+-------------------------|


Υλοποίησα, στον TCP/Server, ένα modbus client που υποστηρίζει μόνο τις παρακάτω functions.

03 (0x03) Read Holding Registers
Βλέπε MODBUS Application Protocol Specification V1.1b σελίδα 19/51
http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf

06 (0x06) Write Single Register
Βλέπε. MODBUS Application Protocol Specification V1.1b σελίδα



Statefull
Ένας WebServer, δημιουργεί για κάθε νέα σύνδεση ένα SessionId που το επιστρέφει στον Client σαν Cookie. Με κλειδί αυτό το SessionId, ο WebServer, συσχετίζει όλη τα δεδομένα του συγκεκριμένου client (user name, db connection κτλ)
Ο ίδιος client, στην επόμενη επικοινωνία του, αποστέλλει το SessionId ώστε να επαναχρησιμοποιήσει τα δεδομένα που όρισε σε προηγούμενη κλήση (statefull).

Περιγραφή του προγράμματος.
Ο server ακούει στην πόρτα 1024 και για κάθε νέα σύνδεση πελάτη δημιουργεί ένα ξεχωριστό thread που θα χειρίζεται την επικοινωνία μεταξύ τους (ServerType= stThreadBlocking). Μόλις αποσταλεί η απάντηση στον πελάτη, το νήμα τελειώνει την εργασία του και επιστρέφει στο pool. Η επόμενη κλήση από τον ίδιο πελάτη δεν θα συσχετισθεί με καμία προηγούμενη (stateless).
Δημιουργούνται νήματα (threads) κλάσης που κληρονομεί από την TServerClientThread και επιστρέφονται στο Server μέσω του event OnGetThread.
procedure TFrmServer.ServerGetThread(Sender: TObject;  ClientSocket: TServerClientWinSocket;   var SocketThread: TServerClientThread);
begin
 SocketThread:=TSrvThread.Create(ClientSocket, Handle);
end;

TSrvThread=class(TServerClientThread)
           private
            fHandle:THandle;
...
           protected
            procedure ClientExecute; override; 
           public
            constructor Create(aSocket:TServerClientWinSocket;
           aHandle:THandle);
           end;


Στην procedure TSrvThread.ClientExecute; διαβάζουμε το εισερχόμενο TCP/IP μήνυμα.

Η ρουτίνα RequestType το κατηγοριοποιεί σαν rtHTTP, rtText, rtModBus ή rtBinary και απαντάει ανάλογα.

Στο http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html περιγράφεται ότι η πρώτη γραμμή ορίζεται σαν

Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
Όπου Method         = "OPTIONS"                ; Section 9.2
                      | "GET"                    ; Section 9.3
                      | "HEAD"                   ; Section 9.4
                      | "POST"                   ; Section 9.5
                      | "PUT"                    ; Section 9.6
                      | "DELETE"                 ; Section 9.7
                      | "TRACE"                  ; Section 9.8
                      | "CONNECT"                ; Section 9.9
                      | extension-method
       extension-method = token


Στο πρόγραμμα κατηγοριοποιώ την κλήση σαν http αν περιέχει το HTTP/1. και ξεκινάει με τις λέξεις GET ή HEAD.

Ο χειρισμός του telnet, είναι διαφορετικός. To socket παραμένει «ανοικτό» έως να έρθει κάποιος control χαρακτήρας.

Το πρόγραμμα χρειάζεται επεκτάσεις εάν κάποιος θέλει να κάνει πραγματική δουλειά με αυτό.

Η πρώτη και σημαντικότερη είναι η υλοποίηση μιας κλάσης TModbus που θα υλοποιεί το modbus πρωτόκολλο έτσι ώστε ο κώδικας (C μεταφιεσμένη σε Pascal) να γίνει συντηρήσιμος.


  rtModBus : begin
         case pbFC^ of
           $03 : begin // mbfReadHoldingRegs = $03;
                  pa:=pword(FRequest);
                  pd:=pa;
                  inc(pa, 4); // points to Start Address
                  inc(pd, 5); // point to count
                  move(FRequest^,data[0],8);
                  cnt:=swap(pd^);
                  data[ 8]:=cnt*2;
                  pd:=pword(@data[9]);
                  for i:=0 to pred(cnt) do begin
                    w:=SendMessage(fHandle,REG_GET,swap(pa^)+i,0);
                    pd^:=swap(w);
                    inc(pd);
                   end;
                  size:=8+1+cnt*2;
                  data[5]:=2 + 1 + cnt*2;
                  ClientSocket.SendBuf(data,size);
                 end;


Μπορείτε να κατεβάσετε την εφαρμογή και το source από το
http://www.filefactory.com/file/dftvw1w1xw7/n/Server01_rar

Και παραφράζοντας τον μαύρο πειρατή (Αστερίξ στη Κορσική σελ. 19)
Bugus humanum est

Καλό φθινόπωρο
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 09 September 2012 :  15:41:35  Show Profile
Σωστή ή λανθασμένη υλοποίηση ;

Μετά από αρκετά χρόνια στην δουλειά βλέπω το λάθος από μακριά. Και από μια παράξενη σύμπτωση όταν ελέγχω την δουλειά κάποιου άλλου σχεδόν πάντα «σκοντάφτω» επάνω του.

Σαν να είναι εκεί και να μου κλείνει το μάτι και να μου λέει «προσπέρνα με».

Στην υλοποίηση του Server έμπλεξα τις λειτουργίες του server με τον χειρισμό των μηνυμάτων. Ο Server διαχειρίζεται tcp/ip συνδέσεις, και «περνάει» τα μηνύματα σε κατάλληλους χειριστές (handler threads).
Εκεί τα πράγματα πρέπει να είναι επίσης καθαρά και την αποκωδικοποίηση της ερώτησης και την κωδικοποίηση της απάντησης πρέπει να την κάνει μια άλλη HTTP ή Telnet ή TModbus κλάση.

Η απάντηση λοιπόν είναι απλή. Ξαναγράψτο. Νέα σχεδίαση, υλοποίηση και τεκμηρίωση.

TModbus
Το modbus είναι ένα πρωτόκολλο ανταλλαγής ερωτήσεων & απαντήσεων και παρέχει μία client/server επικοινωνία μεταξύ συσκευών συνδεδεμένων μέσω TCP/IP. Κάθε ερώτημα περιγράφεται από ένα κωδικό (function code FC). Το modbus πρωτόκολλο απεικονίζει τις διευθύνσεις και τα δεδομένα με το πιο σημαντικό byte πρώτο (big Endian).
Οι 4 κύριοι πίνακες που χρησιμοποιεί το Modbus είναι
Διακριτές είσοδοι, απεικονίζονται σε bits και μπορούμε μόνο να τις διαβάσουμε.
Coils, (solid-state relay) απεικονίζονται σε bits και μπορούμε να τις διαβάσουμε & να γράψουμε
Input Registers, απεικονίζονται σε 16-bit word, μπορούμε μόνο να τις διαβάσουμε.
Holding Registers, απεικονίζονται σε 16-bit word, μπορούμε να τις διαβάσουμε & να γράψουμε
Οι βασικές λειτουργίες είναι
(0x01) Read Coils
(0x02) Read Discrete Inputs
(0x03) Read Holding Registers
(0x04) Read Input Registers
(0x05) Write Single Coil
(0x06) Write Single Register

O modbus Master ρωτάει ή καθορίζει τιμές για εισόδους ή εξόδους ενός PLC. To PCL είναι slave και απαντάει στα ερωτήματα του Master.
Στην δική μας περίπτωση υλοποιούμε αμφοτέρους.

Ο Master/client χρησιμοποιεί ένα socket component για την TCP/IP σύνδεση με τον Server/ Slave. Κατασκευάζει ένα object της κλάσης TModbus και καλεί τις ρουτίνες που υλοποιούν τις βασικές λειτουργίες.

Τα αποτελέσματα εμφανίζονται σε array properties.

Modbus:=TModbus.Create(FSocket);
If Modbus.ReadHoldingRegisters(1,1) then
 Modbus.HoldingRegs[1]
έχει την ζητούμενη τιμή.

Ο Server/Slave έχει ήδη λάβει το TCP/IP μήνυμα και πρέπει να αποφασίσει τον τύπο του.
Χρησιμοποιώντας τον 2ο constructor δημιουργεί ένα object και ελέγχει την ορθότητα του μηνύματος. Σε περίπτωση λάθους στην δομή του μηνύματος, δημιουργεί exception.

Η κλάση TModbus περικλείει (encapsulates) την δομή ενός modbus μηνύματος και ο παρακάτω κώδικας (ισάξιος μόνο για τον Chuck http://blog.magicsoftware.com/2011/09/if-chuck-norris-were-programmer.html )
// check for ModBus request
  if FReqSize>=12 then begin
   pwTI :=pword(p);    // 0..1 Transaction Identifier
   pwPI :=pword(p+2);  // 2..3 Protocol Identifier
   pwlf :=pword(p+4);  // 4..5 Length Field
   pbUID:=pbyte(p+6);  // 6    Unit ID
                       //      MB_IGNORE_UNITID = 255
   pbFC :=pbyte(p+7);  // 7;   Function Code
                       //      mbfReadHoldingRegs = $03;
                       //      mbfWriteOneReg = $06;
                       // 8;   Data

   if ((pbFC^=$06) or (pbFC^=$03)) and (pwlf^=swap($0006)) then begin
    result:=rtModBus;
    exit;
   end;
  end;

αντικαθίσταται με
try
   FModbus:=TModbus.Create(FRequest^,FReqSize);
   FModbus.OnGetData:=ModbusGetData;
   FModbus.OnSetData:=ModbusSetData;
   result:=rtModBus;
   exit;
  except
   Log('Not modbus message');
  end;

Και ο επίσης «ανδρικός» κώδικας
rtModBus : begin
         case pbFC^ of
           $03 : begin // mbfReadHoldingRegs = $03;
                  pa:=pword(FRequest);
                  pd:=pa;
                  inc(pa, 4); // points to Start Address
                  inc(pd, 5); // point to count
                  move(FRequest^,data[0],8);
                  cnt:=swap(pd^);
                  data[ 8]:=cnt*2;
                  pd:=pword(@data[9]);
                  for i:=0 to pred(cnt) do begin
                    w:=SendMessage(fHandle,REG_GET,swap(pa^)+i,0);
                    pd^:=swap(w);
                    inc(pd);
                   end;
                  size:=8+1+cnt*2;
                  data[5]:=2 + 1 + cnt*2;
                  ClientSocket.SendBuf(data,size);
                 end;

αντικαθίσταται με
rtModBus : begin
                   FModbus.PrepareResponse;
                   FModbus.Respond(ClientSocket);
                   FreeAndNil(FModbus);
                  end

και η κλάση ΤModbus ακολουθεί.

TBytes = array of byte;

 TMBAP = packed record { 7 bytes }
           TrnId : word;
           ProtocolIdent: word;
           Len:word;
           UnitId:byte;
          end;

 TPDU  = packed record { 253 bytes }
           FC  : byte;
           Data: packed record case integer of
                  0 : (b: array[0..251] of byte);
                  1 : (w: array[0..125] of word);
                  2 : (o: packed record
                            len : byte;
                            w   : array[0..124] of word;
                            b   : byte;
                           end)
                 end;
          end;

 TADU  = packed record // 260 bytes
           MBAP : TMBAP;
           PDU  : TPDU;
          end;

|Modbus Application Protocol (MBAP) Header | Protocol Data Unit (PDU)|
|------------------------------------------+-------------------------|
|Transaction |Protocol  | Length  | Unit   | Function   |   Data     |
|Identifier  |Identifier| Field   | ID     | Code       |            |
|--------------------------------------------------------------------|
| (2 bytes)  |(2 bytes) |(2 bytes)|(1 byte)|(1 byte)    | Μεταβλητό  |
|------------------------------------------+-------------------------|

 TModbus = class
           Private
             ...
           Public
            // Client Side
            constructor Create(aSocket:TCustomWinSocket); overload;
            // Server Side
            constructor Create(const Request; size:word); overload;  
            procedure   PrepareResponse;
            procedure   Respond(aSocket:TCustomWinSocket);

            function ReadCoils(StartAddress, Count:word):boolean;
            function ReadDiscreteInputs(StartAdd,Count:word):boolean; 
            function ReadHoldingRegisters(RegNo,Count:word):boolean; 
            function ReadInputRegisters(RegNo,Count:word):boolean;
            procedure WriteSingleCoil(Addr:word;value:boolean);
            procedure WriteSingleRegister(RegNo,value:word);

            property  DiscreteInput[idx:integer]:boolean read ...
            property  Coils[idx:integer]:boolean read ...
            property  InputRegs[idx:integer]:word read ...
            property  HoldingRegs[idx:integer]:word read ...

            property  FC:byte read FFC;
            property  Request:TBytes read GetRequest;
            property  Response:TBytes read GetResponse;

            property  OnGetData:TOnExchangeData read ...         
            property  OnSetData:TOnExchangeData read ...

           end;

Νομίζω ότι πλέον γίνεται φανερή η χρησιμότητα των objects.
Περιλαμβάνουν δεδομένα και μεθόδους που υλοποιούν τις υπηρεσίες τους προς τον χρήστη.

Έπρεπε, η κλάση TModbus να ανοίγει και την TCP/IP σύνδεση ?
Αποφάσισα ότι η κλάση θα χρησιμοποιεί connected sockets και θα συνδιαλέγεται με την υπόλοιπη εφαρμογή με καθαρό τρόπο.

Επιπλέον η μνήμη που γράφει ή διαβάζει ο Server/Slave θα ανήκε στον ίδιο, και την διαχειρίζεται, την εμφανίζει ή στην κλάση TModbus που βασικά χερίζεται μόνο τον σχηματισμό των μηνυμάτων. Επέλεξα η μνήμη να ανήκει στον Slave και να η TModbus κλάση να επικοινωνεί με event Handlers
TOnExchangeData = procedure(FC:byte; param1,param2:word;var value:word) of object;

Μπορείτε να κατεβάσετε την εφαρμογή και το source από το
http://www.filefactory.com/file/vjru517ir15/n/Server02.rar

Παρατηρήσεις και σχόλια πάντα δεκτά.

Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 05 October 2012 :  00:59:57  Show Profile
Σειριακή επικοινωνία

Όταν χρησιμοποιήσαμε PSTN γραμμή συνδέαμε το modem στην σειριακή του υπολογιστή και «βγαίναμε» στο internet.
Η σειριακή (RS232) επικοινωνία ήταν κυρίαρχη ανάμεσα σε συσκευές που είχαν να ανταλλάξουν δεδομένα. Σήμερα αντικαθίσταται σταδιακά με το USB των 480 kbits.
Οι 2 σειριακές συσκευές ανταλλάσουν στοιχεία όταν η μία τα ζητήσει από την 2η ή όταν η 2η τα έχει έτοιμα και τα στέλνει χωρίς ερώτηση. Συνήθως οι διάλογοι είναι ερώτηση – απάντηση.
Η σειριακή επικοινωνία βασίζεται σε ένα ηλεκτρικό και σε ένα λογικό πρωτόκολλο.
Παλμοί στο καλώδιο
Πώς θα κωδικοποιήσουμε την πληροφορία ενός byte σε ένα καλώδιο?. Στην σειριακή επικοινωνία δημιουργούμε μια παλμοσειρά σταθερής διάρκειας , με παλμούς για κάθε bit=1 του byte. Προστέθηκαν και κάποιοι επιπλέον παλμοί συγχρονισμού και ανίχνευσης σφάλματος επικοινωνίας και μεταδίδονται στο καλώδιο με συγκεκριμένη ταχύτητα. Χρησιμοποιούμε ένα οποιοδήποτε UART (πχ 8051) με ένα καλώδιο αποστολής (Tx) και ένα καλώδιο λήψης.
http://www.slideshare.net/pantechsolutions/8051-serial-communicationuart
Προφανώς, για λόγους συγχρονισμού οι 2 άκρες πρέπει να ρυθμισθούν στα ίδια χαρακτηριστικά σειριακής επικοινωνίας (ταχύτητα, parity, stop bits).

Ανίχνευση λαθών.
Κάθε byte περιέχει άρτιο ή περιττό αριθμό από 1. Προσθέτοντας ένα ακόμα bit (παλμό) μπορείς να εξασφαλίσεις ότι ο αριθμός των 1 στο συγκεκριμένο byte είναι άρτιος ή περιττός (parity odd / even). Η ανίχνευση λαθών βασίστηκε στο μέτρημα των 1 κατά την λήψη και την επιβεβαίωση της συμφωνίας επικοινωνίας (odd/even parity).
Το UART παράγει interrupts όταν ο καταχωρητής αποστολής (transmit register) είναι άδειος και ο καταχωρητής λήψης (receive register) γεμάτος. Αυτά τα interrupts φτάνουν στον driver του σειριακού που τα προωθεί σαν events στις εφαρμογές.
Έτσι μπορούμε ασύγχρονα να ενημερωθούμε για την λήψη μέσω σειριακής επικοινωνίας ενός χαρακτήρα ή να γράψουμε σε ένα buffer το προς αποστολή μήνυμα και αφήσουμε να αποσταλεί . Όλα αυτά είναι μέρος του λειτουργικού συστήματος και αποτελούν πρόβλημα των οδηγών (drivers) των σειριακών θυρών.
Τελικά έχουμε 2 συσκευές συνδεδεμένες με τουλάχιστον 3 καλώδια (Tx, Rx, Gnd) όπου το Tx της μίας συσκευής συνδέεται στο Rx της άλλης και έχουν κοινό Gnd.

Λογικό πρωτόκολλο.
Ας δούμε ένα πρωτόκολλο επικοινωνίας για φορολογικούς εκτυπωτές (από αυτούς εκδίδονται οι αποδείξεις στα super market).
ftp://ftp.accordgroup.ro/Pentru%20dealeri/Ithaki/PTR%20Dezvoltatori%20de%20Aplicatii/MICRELEC%20ETR%20EL%20JOURNAL%20PROTOCOL.pdf

Κάθε μήνυμα ξεκινά με τον STX (0x02) και τελειώνει με ΕΤΧ (0x03).
+-----+---------------+-----+
| STX |      Data     | ETX |
+-----+---------------+-----+
Όλα τα δεδομένα στα Data είναι ascii χαρακτήρες από 0x32 έως 0xFF.

Tx data packet
<--------------------- Data -------------------><--Checksum -->
+---------+---------+---------+------+---------+---------------+
| Request / Field 1 / Field 2 / .... / Field N /       CC      |
+---------+---------+---------+------+---------+---------------+
Request 1 char

Rx data packet
<--------------------- Data -----------------------------><-Checksum->
+---------+--------+--------+-------+-------+------------+-----------+
|Response /Status 1/Status 2/Field 1/Field 2/..../Field N/    CC     |
+---------+--------+--------+-------+-------+------------+-----------+
Response,status1, status2 2 numeric digits

BYTE CalcChecksum(BYTE *packet)                        
 {                                                      
   BYTE sum = 0;                                        
   int  checklength = strlen(packet) - 2;               
                                                        
   while(check length--) sum += (BYTE) (*packet++);     
   return (sum % 100));                                 
 }


Σχεδίαση

1. Κάθε μήνυμα απαντάται σύμφωνα με την παρακάτω ροή

Sender (host) Receiver (device)
---------------------------------------------------------
     IDLE                           IDLE
     ENQUIRE ---------------------> 
             <--------------------- ACKNOWLEDGE
   Τx PACKET ---------------------> 
             <--------------------- ACKNOWLEDGE
             <--------------------- Rx PACKET
 ACKNOWLEDGE --------------------->
        IDLE                        IDLE

2. Όλα τα μηνύματα αποστολής μορφοποιούνται, πριν αποσταλούν, σε μία ρουτίνα
3. Όλα τα μηνύματα λαμβάνονται από μία ρουτίνα.
4. Χρησιμοποιώ blocking διαδικασία επικοινωνίας.

Η διαδικασία επικοινωνίας περιγράφεται από το παρακάτω διάγραμμα αλλαγής κατάστασης (state transition diagram).
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 05 October 2012 :  01:17:35  Show Profile
και το διάγραμμα.



Η υλοποίηση ακολουθεί σε επόμενο post.
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 09 October 2012 :  17:23:34  Show Profile
Υλοποίηση

Σε όλα τα πρωτόκολλα επικοινωνίας υπάρχει ένα μήνυμα αποστολής και ένα μήνυμα απάντησης και πάντα θα υπάρχουν προβλήματα επικοινωνίας. Η υλοποίηση ενός πρωτοκόλλου πρέπει, απαραίτητα, να λαμβάνει υπόψιν τα σφάλματα (επικοινωνίας ή λογικά) και να χειρίζεται ανάλογα. Κρινόμαστε πρώτα από την λειτουργικότητα και έπειτα από την αξιοπιστία κάθε συστήματος. Από επικοινωνίες με σειριακούς φορολογικούς εκτυπωτές έως τις ανεμογεννήτριες.

TPacket
Ας ξεκινήσουμε με την πρώτη κλάση που υλοποιεί την λειτουργικότητα του πακέτου αποστολής & λήψης.
TPacketType = (ptTx, ptRx);

TPacket = class(TObject)
...
 public
  constructor Create;            overload;
  constructor Create(aReq:char); overload;
  destructor  destroy; override;
  class function CalcCheckSum(const data: string): string;
  class function ChecksumValid(const data:string):boolean;
  property Fields[index:integer]:string read GetField write SetField;
  property Data:string read GetData write SetData;
  property PacketType:TPacketType read fPacketType write fPacketType;
  property RxResposeCode:integer read fRxResposeCode;
  property RxStatus1:integer  read fRxStatus1;
  property RxStatus2:integer read fRxStatus2;
 end;

Όταν θέλουμε να δημιουργήσουμε ένα μήνυμα για αποστολή τότε

tx:=TPacket.Create('b'); // Request = SetVatRates
for i:=1 to 5 do // προσθέτουμε 5 συντελεστές ΦΠΑ
 tx.Fields[i]:=format('%.2f/',[VatRates[i]]);

Χρησιμοποιούμε το property data που προσθέτει και checksum

Όταν λαμβάνουμε ένα μήνυμα
rx:=TPacket.Create;
rx.data:=msg; // χωρίς STX, ETX

τo property RxResposeCode:integer περιέχει την κωδικό επιτυχίας/αποτυχίας.
Το array property fields τα πεδία του μηνύματος.

Χρησιμοποίησα για πρώτη φορά class functions. Χρειαζόμουν μία υπηρεσία (θα μπορούσα να είναι μια απλή βοηθητική function) που ανήκε σε μία κλάση και δεν ήθελα να δημιουργήσω object.

Η κλάση είναι ένα συμβόλαιο που απαριθμεί και περιγράφει υπηρεσίες.
Για να "πάρουμε" αυτές τις υπηρεσίες δημιουργούμε objects μέσω του constructor κάθε κλάσης. Τα objects καταλαμβάνουν μνήμη για τις εσωτερικές μεταβλητές τους και όλα μοιράζονται τον κώδικα functions.

Η ίδια η κλάση μπορεί να μας δώσει υπηρεσίες αρκεί να μην χρησιμοποιεί μνήμη του object. O constructor, που δημιουργεί objects αποτελεί ΤΗΝ class function.

TSession
Ακολουθεί η κλάση της επικοινωνίας. Εδώ υλοποιήσαμε την λογική του διαγράμματος αλλαγής καταστάσεων.
Μελετήστε το και καταλάβετέ το. Ο κώδικας είναι πιστή αντιγραφή του.
TSession = class
... 
 public
  constructor Create(const params:string); // COM1,9600,N,8,1
  destructor destroy; override;
  // <0 Σφάλμα επικοινωνίας
  // 0  Επιτυχία επικοινωνίας & εκτέλεσης της εντολής
  // >0 Σφάλμα πρωτοκόλλου
  function  Communicate(const TxPacket:TPacket; 
                        out RxPacket:TPacket):integer;
 end;



TFiscalPrinter
Και καταλήγουμε στην κλάση που χρησιμοποιεί η εφαρμογή.
Αυτή με την σειρά της χρησιμοποιεί την TSession & την Tpacket για την επικοινωνία με τον εκτυπωτή.

Αλλά οι υπηρεσίες που παρέχει είναι υψηλού επιπέδου, που μπορούν άμεσα να χρησιμοποιηθούν από μία εφαρμογή (application).

TFiscalPrinter = class
.. .
 public
  constructor create(const COMPort:string);
  destructor destroy; override;

  function FiscalTime:TDateTime;
  function SerialNumber:String;
  function TicketNumber:integer;
  function ZNumber:integer;

  procedure OpenDay;        // Logical operation
  procedure CloseDay;       // Issue Z, read EJ

  procedure OpenTicket(tt:TTicketType; const Title:string ='';                 
                       const Seira:string=''; number:integer=0);
  procedure SaleItem(const code, descr, barcode:string; 
                     price, quant, amount:double; vatCat:integer);
  procedure VoidItem(const code, descr, barcode:string; 
                     price, quant, amount:double; vatCat:integer);
  procedure RefoundItem(const code, descr, barcode:string; 
                        price, quant, amount:double; vatCat:integer);
  procedure DiscountItem(const code, descr, barcode:string; 
                     amount:double; vatCat:integer);
  procedure SubTotalTicket;
  procedure SubTotalDiscount(amount:double);

  procedure CancelTicket;
  procedure CloseTicket;
...
 property capEJ:boolean read FcapEJ; // capability has Electronic Journal
 property capSlip:boolean read FcapSlip;// capability has SlipPrinter

 property Header[index:integer]:string read GetHeader write SetHeader;
 property Footer[index:integer]:string read GetFooter write SetFooter;
 property VatRates[index:integer]:string read GetVatRate write SetVatRate;    
 end;


ΥΓ.
1. Η μόνη μου παρατήρηση στο συγκεκριμένο πρωτόκολλο ήταν η λανθασμένη επιλογή του ‘/’ σαν διαχωριστή πεδίων (field separator) διότι μπορεί να υπάρξει και στα δεδομένα ενός πεδίου του μηνύματος.
Θα μπορούσε να χρησιμοποιηθεί ο χαρακτήρας FS = 28 ή 0x 1C

2. Χρησιμοποιώ το async32 component. (open source)
http://www.general-files.com/download/source/gs554ee144h32i0

3. Με αυτό το post τελείωσα με όσα θέματα είχα σκεφθεί να αναλύσω.
Αναμένω προτάσεις και ερωτήσεις.

ΠΦ
Go to Top of Page

Pirate
Advanced Member

Greece
3177 Posts

Posted - 09 October 2012 :  22:32:47  Show Profile  Visit Pirate's Homepage
Τι να πω! Μένω άφωνος!
Πιστεύω ότι τόση ανάπτυξη κώδικα δεν πρέπει να υπάρχει σε άλλο forum ακόμη και του εξωτερικού.
Η δουλειά αυτή Πάνο προορίζεται για τους επόμενους αναγνώστες του forum αυτού που αφενός θα είναι ειδικοί για να μπορέσουν να παρακολουθήσουν όλο αυτό το ξεδίπλωμα και αφετέρου πολύ ειδικοί θα έλεγα!

Εγώ δεν έχω να παρατηρήσω τίποτα διότι δεν έχω ασχοληθεί με modbus και επικοινωνίες γενικώς. Αν χρειασθεί να ασχοληθώ ξέρω αμέσως που θα ανατρέξω μιας και το έχω ήδη αρχειοθετήσει σε 23 σελίδες του acrobat.
Το μόνο που θα πω είναι ότι αυτή η καταπληκτική γλώσσα (Pascal) που την πεθάνανε οι πεθαμεναντζήδες πάντα με συγκινεί!! Ακόμα και όταν την γράφει άλλος και όχι εγώ!


Υ.Σ.
Μη περιμένεις «λαοθάλασσα» να σε ρωτάει και συ να απαντάς. Χτύπησες με την μία πολύ ψηλά τόσο ψηλά που θα πάσχεις από «μοναξιά». Έχε το υπ' όψη σου αυτό. Και πάλι εύγε!


Στίγμα Μαστροκαπετάνιου
Go to Top of Page

pfys
New Member

Greece
105 Posts

Posted - 09 October 2012 :  23:45:40  Show Profile
Καπετάνιε,

Από την αρχή σκόπευα στους νέους μηχανικούς. Αυτούς που στο πολυτεχνείο διδάχθηκαν να λύνουν προβλήματα. Σε αυτούς που τα μαθηματικά ήταν εργαλείο και όχι αυτοσκοπός. Μάθαμε και μαθαίνουνε να λύνουν προβλήματα αναλυτικά (σπάζοντάς τα σε μικρότερα κομμάτια) και συνδυαστικά (να ενώνουν μικρά κομμάτια γνωστής συμπεριφοράς και να δημιουργούν μεγαλύτερα).

Αλλά το λογισμικό, όσο soft και να είναι, απαιτεί σχεδίαση πριν την υλοποίηση.
Αυτό προσπάθησα να τονίσω. Και πλέον οι κλάσεις και τα objects αποτελούν εργαλεία για να γράφουμε καλύτερο λογισμικό.
quote:
πολύ ειδικοί θα έλεγα!

Δεν συμφωνώ για το πολύ ειδικοί. Αν οι μηχανολόγοι και οι ηλεκτρολόγοι πρέπει να γνωρίζουν τους τρόπους μετάδοσης της θερμότητας τότε θα πρέπει να γνωρίζουν να γράφουν και σωστά λογισμικό. Οι μηχανικοί φτιάχνουν συστήματα και όχι οι προγραμματιστές. Η γλώσσα προγραμματισμού αλλάζει αλλά όπως είπε και ο Σωτήρης
quote:
Οι τεχνικες παραμενουν τεχνικες..

Εσύ ήσουν ήδη έτοιμος. Η περιέργειά σου σε είχε ήδη οδηγήσει στο να εξερευνήσεις τον αντικειμενοστρεφή προγραμματισμό. Αλλά να αλλάξεις αυτή την τεράστια unit με τις βοηθητικές functions που ξέρεις πια απέξω είναι δύσκολο.

Που ξέρεις, μπορεί τελικά και να προσελκύσω νέα μέλη μόνο για το λογισμικό. Αυτοί φυσικά θα μετράνε στους Ηλεκτρολόγους.

Θα συνεχίσω να γράφω για διάφορα θέματα που θα επιλέγω με το ίδιο, χαλαρό, ρυθμό.

Δάσκαλε, σε ευχαριστώ θερμά.
Πάνος
Go to Top of Page
  Previous Topic Topic Next Topic  
 Forum Locked
 Printer Friendly
Jump To:
ΘΕΡΜΑΝΣΗ - ΚΛΙΜΑΤΙΣΜΟΣ - ΜΗΧΑΝΟΛΟΓΙΚΑ © 2000 monachos Go To Top Of Page
This page was generated in 0.51 seconds. Snitz Forums 2000