Τετάρτη 11 Νοεμβρίου 2020

Αναθεώρηση 7, Έκδοση 10

 Τελειοποίησα το XMLData αντικείμενο. Η βασική ιδιότητα, xml δίνει το δένδρο σαν κείμενο και παίρνει το δένδρο σαν κείμενο (διαγράφοντας το τρέχον δένδρο). Αυτό γίνεται σε κάθε κλαδί (το οποίο είναι επίσης ένα δένδρο). Μπορούμε να στήνουμε το δένδρο προγραμματιστικά με εντολές αντί να δώσουμε το κείμενο. Τώρα προστέθηκαν και εντολές για διαγραφή. Στο info προστέθηκε το xml2, που δείχνει πως βγάζουμε τα κλαδιά (με δείκτες σε αντικείμενο) σε μια λίστα, όπου τα ταξινομούμε και μετά στήνουμε γρήγορα το δένδρο, αντί να βάζουμε XMLnode, απευθείας με XMLdata.

To XMLNode είναι ένα αντικείμενο που μας δίνει το XMLdata και πάνω σε αυτό κάνουμε προσθήκες σε χαρακτηριστικά (attributes),  προσθήκη σε τιμή, και το βάζουμε σε ένα αρχικά απροετοίμαστο XMLNode του λείπει το XMLNode. Δεν μπορούμε να χειριστούμε κάτι από το XMLNode γιατί είναι κλειστό για μας. Απλά το δίνουμε στο   XMLdata και αυτό το χειρίζεται, μέχρι να το δώσουμε για "πάντα",  και έτσι να κάνουμε το XMLdata να λειτουργεί. Στην ουσία υπάρχει ένα άλλο αντικείμενο εσωτερικά στο XMLData το οποίο έχει διαφορετικό όνομα και αυτό δεν το προσεγγίζουμε άμεσα. Το XMLData εμφανίζεται σαν XMLmono τύπος, είναι ένας Adapter, όπου το αυθεντικό XMLmonoInternal απλά βρίσκεται μέσα του. Όλο το δένδρο είναι φτιαγμένο μόνο με το XMLmonoInternal, αλλά η πρόσβαση σε αυτό γίνεται με τους Adapters. Πχ όταν κάνει αναζήτηση ενός tag (ετικέτας) για να δημιουργήσει μια λίστα, εσωτερικά το XMLmonoInternal χρησιμοποιεί το αντικείμενο Collection (της VB), και το αποτέλεσμα το παίρνει το εξωτερικό αντικείμενο XMLmono, και το μεταγράφει σε αντικείμενο της Μ2000, που μπορούμε να το δουλέψουμε πιο εύκολα (σε αντικείμενο σωρό τιμών). Όταν ζητάμε ένα δείκτη για ένα δένδρο εσωτερικό του δένδρου, τότε το XMLmono φτιάχνει ένα νέο XMLmono (λέγεται και wrapper), και σε αυτό δίνει τον "κρυφό" δείκτη του XMLmonoInternal.

Δείτε πως έχει το πράγμα. Τα αντικείμενα που χειριζόμαστε στη Μ2000 είναι τριών ειδών. Το ένα είναι το Group ή Ομάδα, και λέγεται και αντικείμενο χρήστη γιατί το φτιάχνει όπως θέλει, εδώ χρησιμοποιούμε την τελεία για να δείξουμε τα μέλη της ομάδας που θα χρησιμοποιήσουμε, Το δεύτερο είναι τα αντικείμενα που παρέχει η γλώσσα για να βάζουμε στοιχεία, όπως οι πίνακες, οι δείκτες σε πίνακες (τουπλέ),, οι λίστες, οι σωροί, και άλλα. Αυτά έχουν εντολές που γράφουμε περιφραστικά, είναι ενσωματωμένα στο συντακτικό της γλώσσας. Τέλος έχουμε τα εξωτερικά, τα λεγόμενα COM. Αυτά μπορεί να είναι από το σύστημα ή ιδιωτικά. Ορισμένα αντικείμενα εσωτερικά της Μ2000 μπορούν να χρησιμοποιηθούν από άλλα προγράμματα, όπως η λίστα. Όλα αυτά τα COM αντικείμενα (και οι φόρμες και τα στοιχεία ελέγχου του GUI) τα χειριζόμαστε με τις εντολές Όρισε (Declare), ή και με την Όρισε ΜεΓεγονοτα (Declare WithEvents),   Μέθοδος (Method) και συνδέουμε μεταβλητές με ιδιότητες με την Με (With),. Επειδή αυτά γίνονται ενώ εκτελείται το περιβάλλον, δηλαδή για το περιβάλλον εκτέλεσης είναι Late Bound, η προσπέλαση στα αντικείμενα γίνεται μόνο από τις μεθόδους και τις ιδιότητες που δηλώνονται Pubic στη VB6 ή όποια άλλη γλώσσα (τα COM αντικείμενα μπορούν να γραφτούν σε διάφορες γλώσσες, στην ουσία αυτό που έχουμε είναι γλώσσα μηχανής και σύνδεση με βιβλιοθήκες).  Για να επικοινωνούν τώρα αντικείμενα και κώδικας μέσα από το περιβάλλον (τον εκτελέσιμο κώδικα), υπάρχουν οι μέθοδοι και ιδιότητες που ορίζονται ως Friend, φιλικές, δηλαδή μπορούν να χρησιμοποιηθούν μόνο από τον "ιδιοκτήτη" ή "κατασκευαστή" ενός αντικειμένου. Στην περίπτωσή εδώ από τη Μ2000. Έτσι με αυτό το επίπεδο "ασφάλειας", υπάρχει απομόνωση από το μέρος του προγράμματος σε Μ2000 και του εκτελέσιμου του περιβάλλοντος της Μ2000.

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

Παλαιότερα η Μ2000 ήταν πιο στριφνή με τα αντικείμενα, πχ στη λίστα δεν άφηνε να βάλουμε αντικείμενα COM, όπως γίνεται από αυτήν την αναθεώρηση (βάζαμε μόνο τα στάνταρ και τις ομάδες). Τα αντικείμενα COM όταν συνδέονται με γεγονότα δεν θέλουμε να είναι μετακινήσιμα, δηλαδή θέλουμε να είναι μέρος ενός τμήματος, για να συνδέονται με τις συναρτήσεις εξυπηρέτησης γεγονότων. Επίσης όταν έχουμε ιδιότητες μιας φόρμας δεν θέλουμε να κρατάμε δείκτη προς την φόρμα γιατί δεν θα απελευθερώσει ποτέ τη μνήμη (μέχρι να διαγραφεί και το τελευταίος δείκτης. Και εκεί η Μ2000 κάνει την δουλειά wrapper, δηλαδή έχει το mHandler, ένα αντικείμενο που χρησιμοποιεί για πολλά άλλα αντικείμενα, και είτε μεταφέρει κάποιο άλλο αντικείμενο, είτε δηλώνει με αναφορά σε συγκεκριμένη θέση μεταβλητής, όπου μπορεί να υπάρχει, ή και να αλλάξει το αντικείμενο χωρίς το mHandler να έχει θέμα, ή να χρειάζεται ενημέρωση. Έτσι όταν συνδέουμε ιδιότητες, με κάποιο αντικείμενο, αυτές δεν κρατάνε δείκτη στο αντικείμενο (άρα δεν έχουν επίπτωση στη ζωή του),  αλλά μια αναφορά εκεί που βρίσκεται ο δείκτης (γενικά όταν λέμε για αναφορά λέμε για ένα τρόπο έμμεσης εύρεσης του αντικειμένου, που ενδέχεται να αστοχήσει, ενώ για δείκτη λέμε για ένα τρόπο άμεσης εύρεσης). Μια συνδεδεμένη ιδιότητα είναι αυτή που έχει περάσει από ένα στάδιο ανίχνευσης κωδικού ιδιότητας για δοσμένο όνομα ιδιότητας (έτσι δουλεύει το COM, μέσω της iDISPATCH διεπαφής). Κάθε φορά που πάμε να εκτελέσουμε την ιδιότητα, για διάβασμα ή για γράψιμο (ότι μας αφήνει το αντικείμενο), χρησιμοποιείται ο κωδικός άμεσα (χωρίς να γίνεται αναζήτηση). Ακόμα και στις μεθόδους που δεν συνδέουμε τμήματα και συναρτήσεις τα ονόματα των κλήσεων κρατούνται σε λίστα και άμεσα δίνουν τον αριθμό εκτέλεσης, από την δεύτερη εκτέλεση (η Μ2000 κρατάει λίστες για όλα τα αντικείμενα στη συγκεκριμένη περίοδο εκτέλεσης).

Γενικά οι γλώσσες όπως η VB6 λειτουργούν με tlb βιβλιοθήκες, τiς λεγόμενες Type Libraries. Τα αντικείμενα COM έχουν για τη διεπαφή iDispatch ενσωματωμένη βιβλιοθήκη και η Μ2000 μπορεί να την δείξει. Δείτε το παράδειγμα FS με ποιο τρόπο εμφανίζουμε στη κονσόλα της Μ2000 τις μεθόδους και τις ιδιότητες ενός αντικειμένου  COM.

Αυτά προς το παρόν.

ΓΚ





Δευτέρα 9 Νοεμβρίου 2020

Αναθεώρηση 6, Έκδοση 10 - Διορθώσεις και Επεκτάσεις

Στην 6η αναθεώρηση, διόρθωσα τις επιπτώσεις της 5ης στο τμήμα BASEG, στο αρχείο INFO.GSB, με το οποίο έδειχνα πώς διαβάζουμε recordset απευθείας από το αντικείμενο. Τώρα δουλεύει!


Επίσης βρήκα και ένα πιο παλιό ελάττωμα (πιο ωραία λέξη για το bug), που υπήρχε αλλά σπάνια φαίνονταν. Όταν έβαλα το διά "/" για χαρακτήρα που δείχνει εκτός από το διαίρεση και τις σημειώσεις, το άφησα να το βρίσκει ως έχει το σύστημα, αλλά σε ορισμένες περιπτώσεις που κοίταζε μπροστά, δεν το λογάριαζε. Τώρα έχει διορθωθεί για όλα όπως το "'" και την "\".  Ο χρωματιστής κώδικα τα βρίσκει άμεσα, έχει διαφορετικό τρόπο λειτουργίας (πιο αφαιρετικό). Τώρα συμπίπτει ο χρωματισμός με αυτό που γίνεται (ελπίζω, γιατί πάντα μπορεί να προκύψουν περιπτώσεις...)

Το πρόβλημα το βρήκα μέσω του παραδείγματος S1, στο αρχείο INFO.GSB, όπου στη τελευταία λειτουργία της επίδειξης εμφάνιζε λάθος (έχασε το Επόμενο (Next) σε ένα For Next, γιατί λογάριασε το / ως σημείωση και πέταξε τη γραμμή μαζί με το σημάδι...Επόμενο..). Τώρα το παράδειγμα λειτουργεί.

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

Προσθήκη

Επιπλέον σε αυτήν την αναθεώρηση έκανα προσθήκη στο εργαλείο συμπίεσης. Τώρα μπορούμε να συμπιέζουμε στη μνήμη, δηλαδή να έχουμε φτιάξει το Zip , να μην συμπιέσουμε στο δίσκο,  αλλά να το συμπιέσουμε σε στη μνήμη. Ομοίως μπορούμε από ένα Zip, από την μνήμη ή από τον δίσκο να κάνουμε εξαγωγή αρχείου στη μνήμη ή στο δίσκο. Στο πρόγραμμα JukeBox, η εικόνα έχει πια συμπιεστεί σε αρχείο zip και το αρχείο έχει γίνει κείμενο με κωδικοποίηση Base64 και έτσι συμπεριλαμβάνεται στον κώδικα. Υπάρχουν εντολές που διαβάζουν το κείμενο, το κάνουν δυαδικά δεδομένα και το δίνουν στο αντικείμενο που χειρίζεται zip αρχεία (αλλά και μνήμη που έχει ένα zip αρχείο όπως εδώ), και από αυτά εξάγεται το αρχείο της εικόνας σε άλλη μνήμη (αποσυμπιεσμένο). 

Η Μ2000 έχει την Διάρθρωση μνήμης, ένα αντικείμενο που χειρίζεται μνήμη του υπολογιστή για να έχουμε πρόσβαση σε επίπεδο διεύθυνσης μνήμης. Επίσης χρησιμοποιείται για εικόνες, ήχους, για δυαδικά αρχεία και για να φτιάχνουμε δομές τύπου C (stuctures), τις οποίες τις χρησιμοποιούμε για δυαδικά αρχεία και για να παρέχουμε δομές στο λειτουργικό σύστημα..

Δείτε το DEMO1 όπου χρησιμοποιούνται όλα τα ήδη προγραμματισμού! Είναι αυτό που φτιάχνει ένα απλό 3D γραφικό, και το περιστρέφει. Χρησιμοποιεί διάρθρωση μνήμης για να γράφει τα στοιχεία που περνάει άμεσα σε ένα αντικείμενο μαθηματικών (που έχει η Μ2000) για να γίνονται σειρές υπολογισμών πάνω σε διανύσματα τύπου τετραδόνιου (4D) . Το πρόγραμμα χρησιμοποιεί ένα παράθυρο, και νήματα για διάφορες εργασίες. Έτσι ένα νήμα υπολογίζει την νέα θέση, ένα άλλο νήμα κάνει την εμφάνιση σε τακτά διαστήματα, ένα άλλο σταματάει το πρώτο και βάζει μπροστά ένα άλλο για να βλέπουμε το γραφικό να σταματάει και να αναβοσβήνει! Επιπλέον οι γραμμές του γραφήματος είναι σε αντικείμενο, το οποίο καλούμε μια μέθοδο για να εμφανιστεί. Πριν όμως την εμφάνιση κάνουμε ταξινόμηση, για να γράψουμε πρώτα την γραμμή που είναι στο βάθος.


Στην εικόνα φαίνονται οι τρεις γραμμές (γράφονται με GDI+, με ομαλή γραμμή, χωρίς δοντάκια). Δείτε η μπλε γραμμή σχεδιάστηκε τελευταία, είναι πάνω από τις άλλες. Ο αριθμός πάνω αριστερά (μέσα στο παράθυρο) δηλώνει το φόρτο. Όσο μικρότερος είναι τόσο λιγότερος ο φόρτος του υπολογιστή. Αν κάνουμε εργασίες στον υπολογιστή ή ανοίξουμε πολλές φορές τη Μ2000 και τρέξουμε σε κάθε μία το ίδιο πρόγραμμα τότε το σύστημα αφενός θα τα μοιράσει σε επεξεργαστές και θα μοιραστεί το συνολικό τρέχον φορτίο σε όλους (θα μεγαλώνουν τα νούμερα).

Κάτω στη γραμμή αναφοράς υπάρχουν μετρητές των νημάτων, και το ρολόι. Το εικονίδιο με τις τρεις άσπρες γραμμές ανοίγει το σχετικό μενού απ΄ όπου μπορούμε να χειριστούμε το παράθυρο με τα πλήκτρα ή το ποντίκι, ακόμα και να το μετακινήσουμε με τα βελάκια (σε άλλα παράθυρα που αλλάζουν διάσταση μπορούμε να την αλλάξουμε και από αυτό το μενού). Τα μενού αυτά μπαίνουν και δεξιά. Γενικά τα παράθυρα της Μ2000 είναι πραγματικά παράθυρα, γυμνά, και τα γεμίζει η Μ2000 όπως θέλει. Πάνω δεξιά φαίνεται το "_" για ελαχιστοποίηση στη γραμμή εργασίας, και το τετράγωνο για να κλείσει το παράθυρο. Αν γράφουμε κώδικα και αυτός βγάλει πρόβλημα τότε το παράθυρο κλείνει και μας γυρνάει το σύστημα στη κονσόλα της Μ2000. Εδώ η κονσόλα έχει εξαφανιστεί, και θα επανέλθει μόλις τερματίσουμε το πρόγραμμα. Αν το πρόγραμμα το ξεκινούσαμε με διπλό κλικ (εφόσον δηλώσουμε στο λειτουργικό να ανοίγει ο τύπος αρχείου που θέλουμε με τη Μ2000) τότε δεν ανοίγει η κονσόλα αν δεν το θέλουμε και αν έτσι έχουμε "εφαρμογές" που δεν φαίνεται το περιβάλλον Μ2000 πάνω στο οποίο τρέχουν.


Για τις ημέρες της καραντίνας, ακούμε μουσική με το Jukebox της Μ2000!


ΓΚ


Σάββατο 7 Νοεμβρίου 2020

Revision 5, Version 10

1. A fix in Volume, and some work internal, we have a new example, then Jukebox in info, for playing mp3 like a juke box. We can program by using numbers the tracks we want to play aftet the current one, we can play one or a list in a loop, using a simple interface. In this module we handle the console as window. We can move it around, and using F10 we can maximize or restore it. Except for the movement of console, all other inputs are through keyboard only.

2. I wrote an xml parser in an object (simple one), and works fine. So I made two adapters objects to handle it through the M2000 code.

3. I make an adapter object for cZipArchive, a very good class for handling zip files, using dedicated machine code for the job. ZipArchive


This is the code from XML  module (the demo in info file)

We make this xml

<?xml version="1.0" encoding="UTF16"?>

<names>

    <element Id="69">

        <codename>VB6</codename>

        <sold>2</sold>

    </element>

    <element Id="70" Nobel="yes">

        <codename>C++</codename>

        <sold>3</sold>

    </element>

    <element Id="71">

        <codename>M2000</codename>

        <sold>10</sold>

    </element>

</names>

We use a simple interface to add nodes.


form 80, 48
declare xml xmlData
With xml, "LastChild" as Child
with xml, "xml" as doc$, "beautify" as beautify
with xml, "childcount" as xml.count, "anychild" as xml.child()


print type$(xml)="XmlMono"  ' true
method xml, "PrepareNodeSimple", "xml" as ProcessInstructions
// nodes are not xmlmono types
print type$(ProcessInstructions)="XmlNode"


method xml, "PlaceAttributeToNode", ProcessInstructions, "version", "1.0"
method xml, "PlaceAttributeToNode", ProcessInstructions, "encoding", "UTF16"


// the first node we build used for the processinstructions
method xml, "PlaceProcessingInstructions", ProcessInstructions


method xml, "PrepareNode", "names" as Node
method xml, "InsertNode", Node
// so now our xml has a name "names"


// now we make a new one, pace an attribute and place it as a child
method xml, "PrepareNode", "element" as Node1
method xml, "PlaceAttributeToNode", Node1, "Id", "69"
method xml, "AppendChild", Node1
// we can get the XmlMono from that list child


Print type$(Child)="XmlMono"
// our child hasn't a node, so we have to make one and then insert to it
method xml, "PrepareNode", "codename","VB6" as Node
method Child, "AppendChild", Node
// we can append a child to this child
method xml, "PrepareNode", "sold","2" as Node1
method Child, "AppendChild", Node1 ' see we call AppendChild to Child object


with child, "tag" as child.tag$, "attr" as child.attr$(), "text" as child.text$, "xml" as child.doc$, "anychild" as child.child()
with child,"childcount" as child.count
Print child.tag$
Print child.attr$("Id")
Print child.text$
Print child.doc$
declare anychild xmldata
with anychild, "text" as anychild.text$, "tag" as anychild.tag$
    for i=0 to child.count-1
        anychild=child.child(i)
        Print anychild.tag$;"=";anychild.text$
    next


// we repeat the element here


method xml, "PrepareNode", "element" as Node1
method xml, "PlaceAttributeToNode", Node1, "Id", "70"
// we can place additional attribudes
method xml, "PlaceAttributeToNode", Node1, "Nobel", "yes"
method xml, "AppendChild", Node1
// we can get the XmlMono from that list child


Print type$(Child)="XmlMono"
// our child hasn't a node, so we have to make one and then insert to it
method xml, "PrepareNode", "codename","C++" as Node
method Child, "AppendChild", Node
// we can append a child to this child
method xml, "PrepareNode", "sold","3" as Node1
method Child, "AppendChild", Node1 ' see we call AppendChild to Child object


    for i=0 to child.count-1
        anychild=child.child(i)
        Print anychild.tag$;"=";anychild.text$
    next


// we repeat the element here
method xml, "PrepareNode", "element" as Node1
method xml, "PlaceAttributeToNode", Node1, "Id", "71"
method xml, "AppendChild", Node1
// we can get the XmlMono from that list child
Print type$(Child)="XmlMono"
// our child hasn't a node, so we have to make one and then insert to it
method xml, "PrepareNode", "codename","M2000" as Node
method Child, "AppendChild", Node
// we can append a child to this child
method xml, "PrepareNode", "sold","10" as Node1
method Child, "AppendChild", Node1 ' see we call AppendChild to Child object


    for i=0 to child.count-1
        anychild=child.child(i)
        Print anychild.tag$;"=";anychild.text$
    next


// part2
// we make same new xmlData object
declare Child1 xmlData


declare nChild xmlData
With nChild, "Attr" as nChild.Attr$()


for i=0 to xml.count-1
    nchild=xml.child(i)
    Print nChild.Attr$("Id")
next


function getnextchild(xml, &xmlmono) {
    method xml, "EndOffChilds", &xmlmono as ok
    = ok
}


// Here we get the result from LastChild, if we use AS and not Set we get the property
// but here we need the original pointer
// so when we change the pointer the connected propeties made them with With statement,
// these follows the object ponter.
// (when we use AS we connect the properties with a property of another object, so when we change that object, the other properties follow)
// (you see that in the For Next blocks after each element append to xml)
With xml, "LastChild" set Child1
with child1, "tag" as child1.tag$, "attr" as child1.attr$(), "text" as child1.text$, "xml" as child1.doc$, "anychild" as child1.child()
declare inner xmlData
with inner, "tag" as inner.tag$, "text" as inner.text$


method xml, "ResetChildPointer"
// here we pass a xmlData object and we get another
While getnextchild(xml, &child1)
    method child1, "ResetChildPointer"
    Print child1.attr$("Id"), child1.tag$,
    While getnextchild(child1, &inner)
    Print inner.tag$, inner.text$
    end while
end While


''''''''''''''''''''''aaaaa
Pen 15 {Print "Using negative beautify, we set identation to 0 for first level"}


beautify=-4
Print #-2, doc$
Print "Press a key"
Push key$: Drop
cls
Pen 15 {Print "Using positive beautify, we set identation same for all levels"}
beautify=4
Print #-2, doc$
declare L xmlData
with L, "xml" as L.doc$,"childcount" as childrens
L.doc$=doc$
Pen 15 {Print "By default beautify=0 so we get all text in a row"}
Report 3, L.doc$
Print childrens=3
child1=xml.child(2) ' 3rd
child1=child.child(1) ' 2nd


Print child1.doc$
child1.text$=str$(val( child.text$)+1, "")
Print child1.doc$
method xml, "GetListByTag", "codename" as list_in_a_stack_object
Print "Found: ";Len(list_in_a_stack_object)
For i=1 to Len(list_in_a_stack_object)
    child1=stackitem(list_in_a_stack_object, i)
    Print "codename:";child1.text$
next
// another way
one=each(list_in_a_stack_object)
while one
    child1=stackitem(one)
    Print "codename:"; child1.text$
end while


method xml, "GetListByTag", "element" as list_in_a_stack_object
one=each(list_in_a_stack_object)
with child1, "textFromChild" as child1.child.text$()
while one
    child1=stackitem(one)
    nobel$=child.attr$("Nobel")
    Print "Id="+child1.attr$("Id"), if$(nobel$=""-> "","Nobel="+nobel$)
    Print "codename:"; child1.child.text$("codename"), "sold: ";child1.child.text$("sold")
end while


declare xml nothing



Τετάρτη 28 Οκτωβρίου 2020

Αναθεώρηση 3 και 4, Έκδοση 10 και παράδειγμα με αρχείο xlsx

Αν δεν υπάρχει η βιβλιοθήκη για αρχείο Access τύπου 12, τότε κατεβάζουμε από εδώ

Microsoft Access Database Engine 2016 Redistributable


Σε αυτές τις αναθεωρήσεις έφτιαξα δυο προβληματάκια. Ένα είχε να κάνει με handlers του συστήματος γραφικών. Σε μια λειτουργία είχα ξεχάσει μια γραμμή να τη σβήσω, με συνέπεια να βγάζει έναν νέο handler χωρίς να υπάρχει αντίστοιχη εντολή για διαγραφή. Κάθε εφαρμογή των Win32, έχει όριο τους 10000 handlers. Μόλις τους φτάσει "καθάρισε". Μπορεί κανείς να εποπτεύει τη χρήση τους από τη διαχείριση διεργασιών.

Παράδειγμα με αρχείο xlsx

Επειδή δεν έχω excel στον υπολογιστή, για να το ανοίξω ως COM αντικείμενο και να πάρω τις τιμές από το πρόγραμμα, θα το ανοίξω με χρήση ODBC, όπου δηλώνω το τρόπο πρόσβασης στο λεγόμενο αλφαριθμητικό σύνδεσης (connection string). Οι εντολές για βάσεις δεδομένων παίρνουν πρώτο όρισμα ή το όνομα της βάσης ως αρχείο mdb, ή σε παρένθεση ένα όνομα που θέλουμε και για τον διερμηνευτή σημαίνει ότι έχουμε δώσει τη σύνδεση στο αλφαριθμητικό σύνδεσης.

Επειδή οι στήλες Unit QTY και QTY έχουν και αριθμούς και αλφαριθμητικά δίνουμε στο σύνδεση το χαρακτηριστικό IMEX=1 με το οποίο όπου βρίσκει αλφαριθμητικά και αριθμούς σε στήλες τα κάνει αλφαριθμητικά. Εδώ μόνο η Item QTY είναι καθαρή, δηλαδή αριθμός. 

Υπάρχουν δυο προγράμματα, το ένα δίνει την εικόνα παρακάτω και το άλλο κάνει εκτύπωση (επιλογή σε pdf). Υπάρχει το αρχείο parts.xlsx καθώς και ένα zip αρχείο με την εξαγωγή (φαίνεται στη δεύτερη εικόνα).


Αρχείο parts.xlsx



// set the output console to 60X48
Form 60, 48
Cls 15, 0 ' White
Back {Cls 15,0}
Pen 0 ' Black
Double
Report 2, {Exporting Data from an xlsx file}
Normal
Italic 1
Report 2, {from Autodesk Inventor application}
Italic 0
// This is the original file that we open through ODBC driver
f$=dir$+"parts.xlsx"
Print Part $(9),"File: ",@(tab); f$
Print
// We have a excel file created from Inventor for parts of an assembly
// set the output to left justification, and adjust all letters/numbers to left border, also cut excluding letters from column width
// code 9 not use word cut but cut in letters
// see bellow where we place 60 for a temporary column definition in a $(9,60) in a Print Part
Print $(9)


file$=quote$(f$)
A$={Provider=Microsoft.ACE.OLEDB.12.0;Data Source=}+file$+{;Extended Properties="Excel 12.0 Xml;ReadOnly=0;HDR=YES; IMEX=1";}  ' all as text
Print "Connection string:",
Pen 5 {Report 3,A$, width-pos-4}
Cursor 0
Report 2, "File Structure Analysis"
db.provider a$,"ODBC",""
flush
Structure "(anyname)"
// We use Stack with no parameters to view the items of stack
// here the items are 1 "BOM$" 0
Print "Values in Stack:", : Stack
// so first item is number of tables, and the Next items are table name and number of indexes
// for each table, but here we get the first one
Read TablesNum
// If TablesNum=0 Then we get error
//Print TablesNum
Read Tablename$, TableIndexes
//Print Tablename$, TableIndexes
// So we flush all other values for other tables
Flush
// So now we get the structure of table Tablename$
// we have place IPEX=1 so we get TEXT where the column has mix types (strings and arithmetic)
Report 2, "Table Structure Analysis"
Structure "(anyname)", Tablename$
Read Many, drop1
Print $(4),
For i=1 to Many
      Read FieldName$, Type$, Bytes
      Print i, "["+FieldName$+"]", @(tab(3)), Type$, Bytes
Next i
Print $(9),
Retrieve "(anyname)", "Select * from ["+Tablename$+"]", 1,"",""
// Stack
Read Many
Print "Records:";Many
// remove all data from stack
flush
// because we use
Report 2, "Search for Part Number 0897-PA-0114-003"
Check("0897-PA-0114-003")
Report 2, "Search for items with Material from Steel, no assemblies, only parts, and display part numbers"
Search "(anyname)", Tablename$, 1,"Material", "Like", "%Steel%"
Read Many
i=1
// Using code 4 we place strings left justification but maybe using more than one columns
Print $(4),
Counter=0
Inventory AllItems
While Many>0
      Drop 2
      Read Ret$
      If Val(left$(Ret$, 3)) Else
            Counter++
            If Exist(AllItems, Ret$) Then
                  Return AllItems, Ret$:=AllItems(Ret$)+1
            Else
                  Append AllItems, Ret$:=1
            End If
      End If
      Flush
      many--
      i++
      If many Then
            Search "(anyname)", Tablename$, i,"Material", "Like", "%Steel%"
            Drop       
      End If
End While
Print
Print "Total:"; Counter
Report 2,"Group, Sort and Display"
Sort AllItems
Items=Each(AllItems)
i=1
While Items
      Print Part $(7, 4),~(7), Str$(i, "000"), $(4), ~(6), Eval$(Items!);~(3);" x ";Eval(Items)
      Print
      i++
End While
WaitKey$=Key$
Close base "(anyname)"
Sub Check(a$)
      SearchPart(a$, 1)
      Read Many
      If Many>0 Then
            local m$=""
            Read local codelist$
            Flush
            if many>1 then
                  local i
                  for i=2 to many
                        SearchPart(a$, i)
                        Drop
                        codelist$+=", "+Letter$
                  next i
                      m$="s x"+Str$(Many)
            end if
            
            ViewOne(codelist$, m$) ' all the values are in stack
      Else
            flush
      End If
End Sub
Sub SearchPart(a$, i)
Retrieve "(anyname)", Tablename$, i,"Part Number", a$
End Sub
Sub CheckMany()
      //stack
      Read Many
      If Many>0 Then
            Print "Found :";Many
            flush
      End If
End Sub
Sub ViewOne(AllItems$, m$)
      Pen 4 {
            Read Local Filename$, Part$, BOM$, Item
            Read Local Unit$,Qty$,StockNum$, Desc$, Mass$, Material$, Project$, Rev$
            PrintValue("Item"+m$, AllItems$)
            PrintValue("File", Filename$)
            PrintValue("Part", Part$)
            PrintValue("BOM", BOM$)
            PrintNumValue("Item Qty", Item)
            PrintValue("Unit Qty", Unit$)
            PrintValue("Qty",Unit$)
            PrintValue("StockNum", StockNum$)
            PrintValue("Desc", Desc$)
            PrintValue("Mass", Mass$)
            PrintValue("Material", Material$)
            PrintValue("Project", Project$)
            PrintValue("Rev", Rev$)
      }
End Sub


Sub PrintNumValue(A$, B)
      Pen 1 {
            Print A$,
      }
      Print Part @(tab),$(4); B
      Print
End Sub
Sub PrintValue(A$, B$)
      Pen 1 {
            Print A$,
      }
      Print Part @(tab), $(9, 60), B$
      Print
End Sub


Και εδώ είναι η εκτύπωση σε Pdf:




Zip αρχείο με το pdf.

flush
f$=dir$+"parts.xlsx"
file$=quote$(f$)
A$={Provider=Microsoft.ACE.OLEDB.12.0;Data Source=}+file$+{;Extended Properties="Excel 12.0 Xml;ReadOnly=0;HDR=YES; IMEX=1";}


db.provider a$,"ODBC",""


structure "(anyname)"
Read TablesNum, Tablename$, TableIndexes
Flush
structure "(anyname)", Tablename$
read Many, drop1
Retrieve "(anyname)", "Select * from ["+Tablename$+"]", 1,"",""
Read Many
flush
Printer {
      Linespace 0
      Form 80,66
      Pen 0
      Double
      Report f$
      Normal
      Cursor 0,3
      PageNo=1
      for i=1 to Many
      Layer {Print i: refresh}
      st$="Select * from ["+Tablename$+"]"
      Retrieve "(anyname)", st$, i,"",""
      Drop ' drop an item from stack (the count number of records, same as Many variable)
      Read Item$, Filename$, Part$, BOM$, Item
      Read Unit$,Qty$,StockNum$, Desc$, Mass$, Material$, Project$, Rev$
      Print Over ~(15,7,7), "A/A ["; I;"]"
      Print Under
      ViewOne()
      if i mod 4=0 and i<many then
            Cursor 0, height-1
            Print Part $(6, width), "- "+Str$(PageNo,"")+" -"
            PageNo++
            Page
            cursor 0,0
            Double
                  report f$
            Normal
            Cursor 0,3
      end if
      wait 10
      Next
      i--
      if i mod 4<>0 then
            Cursor 0, height-1
            Print Part $(6, width), "- "+Str$(PageNo,"")+" -"
      end if
}


wait 2000
CLOSE BASE "(anyname)"


Sub ViewOne()
            Print $(9),
            PrintValue("Item", Item$)
            PrintValue("File", Filename$)
            PrintValue("Part", Part$)
            PrintValue("BOM", BOM$)
            PrintNumValue("Item Qty", Item)
            PrintValue("Unit Qty", Unit$)
            PrintValue("Qty",Unit$)
            PrintValue("StockNum", StockNum$)
            PrintValue("Desc", Desc$)
            PrintValue("Mass", Mass$)
            PrintValue("Material", Material$)
            PrintValue("Project", Project$)
            PrintValue("Rev", Rev$)
            Print $(4),
End Sub
Sub PrintNumValue(A$, B)
      Pen 1 {
            Print A$,
      }
      Print Part @(tab),$(4); B
      Print
End Sub
Sub PrintValue(A$, B$)
      Pen 1 {
            Print A$,
      }
      Print Part @(tab), $(9, 60), B$
      Print
End Sub


Παρασκευή 23 Οκτωβρίου 2020

Αναθεώρηση 1, Έκδοση 10.

 Σε αυτήν την αναθεώρηση έγιναν μικρές προσθήκες και διορθώσεις. Επιπλέον ανέβηκε στο dropbox, γιατί η έκδοση 10 δεν είχε ανέβει (υπήρχε μόνο στο github),



Από το Readme.txt, επιγραμματικά στα ελληνικά. 

1. Διορθώθηκε η συνάρτηση Τραπ()  ή αλλιώς η στρογγύλευση του τραπεζίτη. Από λάθος έβγαινε πάντα θετική τιμή!

2. Μπήκε στα παράθυρα του χρήστη στο μενού ελέγχου η εντολή για αλλαγή μεγέθους από το πληκτρολόγιο (υπήρχε η αλλαγή θέσης). Οπότε ανοίξτε από το info το cs (διορθωτής c sharp) για να δείτε το μενού ελέγχου (πάνω αριστερά με τρεις οριζόντιες μικρές γραμμμές).

3. Η φόρμα της βοήθειας κάποιες φορές έβγαινε εκτός παραθύρου με συνέπεια να δουλεύει το alt+F4 για κλείσιμο (μετά άνοιγε κανονικά). Τώρα διορθώθηκε ώστε ποτέ να μην μεγαλώνει έξω από την οθόνη (από την πλέον αριστερή, από τις όποιες οθόνες)

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

5. Στην εντολή Ήχος δίναμε όνομα αρχείου. Τώρα μπορούμε να δώσουμε και διάρθρωση μνήμης, φορτωμένη με αρχείο ήχου (wav) από το δίσκο (ή φτιάχνουμε δικό μας αν θέλουμε).

6, Η Ηχογράφηση υπήρχε στο κώδικα αλλά δεν ήταν γνωστή, ήθελε δουλειά και δεν την είχα τελειώσει. Δείτε την βοήθεια για αυτή την εντολή.

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

8. Έγιναν επεμβάσεις και στην εντολή Ταινία (και στο σύστημα γενικότερα του video).

9. Μπήκαν νέα προγράμματα στο Info. Από αυτά περισσότερο θα αναφέρω το console και το sprites. Το δεύτερο το έδειξα και στην παρουσίαση στο συνέδριο Η Πληροφορική στην Εκπαίδευση (12th CIE2020). Το cosnole είναι μια πρόταση για χρήση της κονσόλας των windows μέσα από την Μ2000, 

Στη φωτογραφία φαίνεται η κονσόλα που έχει ανοίξει μπροστά από την κονσόλα της Μ2000 σε καθορισμένη θέση (με εντολή μέσα από την Μ2000). Επίσης  βλέπουμε δυο ειδών prompt (εισαγωγικού μάρκου), το M2000> που δέχεται εντολές για τη Μ2000, στην ουσία τμήματα της Μ2000, και σε αυτή την φάση έχουμε μπλοκάρισμα ενός νήματος που παίζει στο περιθώριο (μπλοκάρισμα όταν διαβάζει η Μ2000 μέσω κονσόλας, επειδή διαβάζει γραμμές). Το δεύτερο μάρκο βγαίνει όταν ανοίξουμε μέσα στην ίδια κονσόλα το cmd.exe ή άλλη εφαρμογή, επειδή η άλλη εφαρμογή τρέχει σε δικό της χώρο! Τότε βλέπουμε το νήμα να εκτελείται κανονικά από πίσω! Και στη πρώτη περίπτωση αν τυπώνουμε μόνο στη κονσόλα δεν έχουμε μπλοκάρισμα. Έχει αφαιρεθεί το Χ από την πάνω αριστερή γωνία. Όμως αν κάποιος πατήσει το ctrl+c θα "ρίξει" και τη κονσόλα και τη Μ2000  από πίσω. Δίνοντας exit, γυρνάμε από το cmd στο αρχικό prompt και μετά πάλι με exit, ή με κενή γραμμή γυρνάμε στη κονσόλα της Μ2000. Από την κονσόλα των Windows η Print τυπώνει στην κονσόλα της Μ2000! για το λόγο αυτό φτιάχτηκε ένα τμήμα με όνομα Echo  για να εμφανίζει διάφορα νούμερα από τη Μ2000, στη κονσόλα.

Με κατάλληλο χειρισμό των αρχείων εισόδου και εξόδου μπορούμε να κάνουμε την Μ2000 να δουλεύει για redirection...ανακατεύθυνση εξόδου!





Τα RTL γράμματα δεν φαίνονται στη σωστή σειρά, αλλά μπήκαν εδώ για να φανεί ότι η κονσόλα των Windows παίρνει Unicode (πολύ περισσότερους από 256 διαφορετικούς χαρακτήρες).


10. Sprites demo:

Τα μεγαλώνουμε, τα γυρίζουμε (περιστρέφουμε ή κάνουμε οριζόντια αλλαγή - flip), τα μετακινούμε. Υπάρχει ένα σχέδιο και σε κάθε ένα ρίχνουμε ένα μερικώς διάφανο χρώμα και το χρωματίζουμε! Επίσης μπορούμε να γράψουμε πάνω στην εικόνα του sprite. Το χεράκι είναι και αυτό ένα sprite, και παίζει στη θέση του δείκτη του ποντικιού τον οποίο έχουμε αφαιρέσει!




Το αυθεντικό από το Readme.txt

1. Remove a bug from Bank() function (banker rounding) which casue a removing the minus sign

2. Added size command for controlbox for user forms (so now we can resize a form by using keyboard)

3. Prevent help form to get resize outside the screen

4. Volume statement works in Window 10, and now has a second parameter to reduce volumen from left or right channel

   Also Volume read only variable now reflect the original volume for the specific run, which show the windows control panel.

5. Sound statement now can use buffer from memory to play sounds.

6. SoundRec statement for recording (program in help file)

7. SoundRec.level  as a read only variable

8. Movie statement fix for Windows 10

9. A new example in Info file, console, or how we can use console (same as cmd.exe console) from M2000, using Win 32 api from M2000 code.

10. Sprites a demo as used for my contribution to a CIE 2020 12th Conference on Informatics in Education