Δευτέρα, 21 Σεπτεμβρίου 2020

Αναθεώρηση 55 'Εκδοση 9.9

 Έγιναν μικρές διορθώσεις, και προστέθηκαν:

3 συναρτήσεις στους αυτόματους πίνακες (tuple)

2 αντικείμενα: ShellPipe και SerialPort.

Το ShellPipe χρησιμεύει για να εκκινήσουμε μια εφαρμογή των windows σε κονσόλα, χωρίς να φαίνεται η κονσόλα και με την εξαγωγή να την παίρνουμε στο πρόγραμμά μας (Μ2000) και από αυτό να μπορούμε να στέλνουμε επίσης οδηγίες. Υπάρχουν στο info τρία παραδείγματα:

Handler - χωρίς τη χρήση του ShellPipe αλλά με τη χρήση του WScript.Shell αντικειμένου και την μέθοδο Exec, ανοίγουμε το cmd.exe και επειδή με αυτό το τρόπο εμφανίζεται η κονσόλα με το cmd.exe (χωρίς όμως να δείχνει κάτι, επειδή ό,τι έχει μας το δίνει στο πρόγραμμα), χρησιμοποιούμε δυο συναρτήσεις του API Win32 για να πιάσουμε το παράθυρο και να το ελαχιστοποιήσουμε στην γραμμή εργασιών. (αυτό τρέχει και σε παλαιότερες εκδόσεις).

Handler2 -  με χρήση του ShellPipe. Τώρα η εφαρμογή cmd.exe δεν φαίνεται στη γραμμή εργασιών, δεν ανοίγει καν το παράθυρο. Όλος ο χειρισμός γίνεται εσωτερικά. Χρησιμοποιούμε και γεγονός για να τραβάμε τα στοιχεία "εξόδου".

Chessgame - με χρήση του ShellPipe (το είχα φτιάξει και με το πρώτο τρόπο αλλά δεν μου άρεσε που φαίνονταν για μερικά χιλιοστά του δευτερολέπτου η κονσόλα της καλούμενης εφαρμογής). Εδώ καλούμε μια μηχανή σκακιού. Η μηχανή μπορεί να κατέβει από εδώ https://stockfishchess.org/

Χρησιμοποίηση τη stockfish 12, για λιγότερο από δευτερόλεπτο, και παίζει πάρα πολύ καλά (παίζει με τα μαύρα). Το ωραίο με το chessgame είναι ότι μπορούμε να πάμε όσες κινήσεις θέλουμε πίσω. Επειδή λειτουργεί εσωτερικά με σειρές FEN,ήταν πολύ εύκολο να συνδέσω την μηχανή για σκάκι. Τις κινήσεις στη σκακιέρα τις ελέγχει το πρόγραμμα σε Μ2000. Όταν είναι να παίξει ο μαύρος, τότε στέλνει την σειρά FEN στη μηχανή και με αίτημα για μερικές εκατοντάδες χιλιοστά του δευτερολέπτου, γυρνάει την απάντηση. Επειδή τις κινήσεις τις γράφει στο φόρμα, αν δεν δούμε τη κίνηση του μαύρου,, τη διαβάζουμε από κάτω. Όταν πηγαίνουμε κινήσεις πίσω τότε όταν θα έρθει η σειρά του μαύρου θα παίξει η μηχανή.
Δεν έχω καταφέρει να νικήσω την μηχανή...(είναι πολύ καλή). Αν ο υπολογιστής είναι αργός ενδέχεται να χάσει η μηχανή, γιατί για ίσο χρόνο ο αργός θα δώσει χειρότερη απάντηση.


Το SerialPort, είναι αντικείμενο για να χειριζόμαστε με απλό τρόπο σειριακές πόρτες. Σκοπός του αντικειμένου είναι να συνδέουμε μια πλακέτα με Arduino. Το έχω δοκιμάσει και δουλεύει τόσο για να διαβάζει όσο και για να γράφει (τα κάνει και τα δύο μαζί). Το Arduino στέλνει δεδομένα σε UTF8, οπότε στο πρόγραμμα Arduino δείχνω πως διαβάζω από τη σειριακή, πως βρίσκω την αλλαγή γραμμής, πως χωρίζω το 'πακέτο" που διάβασα βάσει της αλλαγής γραμμής. Εδώ επειδή έχουμε UTF8, οι χαρακτήρες κάτω από 128 είναι του ενός byte.

Χρησιμοποιήθηκε το παρακάτω σε ένα Arduino NodeMCU 0.9 (ESP-12 Module).

int incomingByte = 0; // for incoming serial data

void setup() {
  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    // say what you got:
    Serial.print("I received: ");
    Serial.println(incomingByte, DEC);
  }
  // δοκιμή και χωρίς τη γραμμή παρακάτω
  Serial.println("Wait Γιώργο....");
}




Δευτέρα, 14 Σεπτεμβρίου 2020

A LED CLOCK

This is a LED clock. Using Desktop statement we can make one of standard colors (0 to 15) as transparent. Here we make Black as transparent.






Module Led_Clock{
      Escape Off
      Smooth off
      Dim D(-1 to 9)
      D(-1)=(0,0,0,0,0,0,0)
      D(0)=(1,1,1,0,1,1,1)
      D(1)=(0,0,1,0,0,1,0)
      D(2)=(1,0,1,1,1,0,1)
      D(3)=(1,0,1,1,0,1,1)
      D(4)=(0,1,1,1,0,1,0)
      D(5)=(1,1,0,1,0,1,1)
      D(6)=(1,1,0,1,1,1,1)
      D(7)=(1,0,1,0,0,1,0)
      D(8)=(1,1,1,1,1,1,1)
      D(9)=(1,1,1,1,0,1,1)
      N=180
      XX=(scale.x-N*75) div 2
      YY=scale.y-N*22
      NN=N
      BackColor=0
      CLS BackColor,0
      desktop 255, BackColor
      Forecolor=12
      C=BackColor-Forecolor
      pen BackColor
      for i=0 to 9: cc=d(i): cc*=c:next
      m=1
      move XX+N*23.2, YY+N*5.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      move XX+N*23.2,YY+N*13.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      move XX+N*49.2,YY+N*5.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      move XX+N*49.2,YY+N*13.2
      polygon BackColor-C, N,-N, N,N, -N, N, -N, -N
      dsk=True
      every 1000/2 {
            k=now
            k1=val(str$(k, "hh"))
            k2=val(str$(k, "nn"))
            k3=val(str$(k, "ss"))
            LED(XX, D(k1 div 10))
            LED(XX+N*12, D(k1 mod 10))
            LED(XX+N*26, D(k2 div 10))
            LED(XX+N*38, D(k2 mod 10))
            LED(XX+N*52, D(k3 div 10))
            LED(XX+N*64, D(k3 mod 10))
            refresh 1000
            if keypress(32) then
                  dsk~
                  if dsk then       desktop 255 else desktop 255, BackColor
            end if
            if keypress(27) or mouse=2 then exit
      }
      desktop 255
      pen 14
      refresh 50
      wait 1000
      Escape On
      sub LED(XX, S())
            move XX+N*1.2, YY+NN
            \\ LED  - UPPER
            polygon BackColor-S(0), N,-N,N*6,0, N,N, -N, N,-N*6,0, -N, -N
            \\ LED | LEFT UPPER
            move XX+N*1.2-N*1.2, YY+N*1.2+NN
            polygon BackColor-S(1), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            move XX+N*1.2+N*7.2, YY+N*1.2+NN
            \\ LED | RIGHT UPPER
            polygon BackColor-S(2), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            move XX+N*1.2, YY+N*8.4+NN
            \\ LED - MIDDLE
            polygon BackColor-S(3), N,-N,N*6,0, N,N, -N, N,-N*6,0, -N, -N
            \\ LED | LEFT BOTTOM
            move XX+N*1.2-N*1.2, YY+N*9.6+NN
            polygon BackColor-S(4), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            \\ LED | RIGHT BOTTOM
            move XX+N*1.2+N*7.2, YY+N*9.6+NN
            polygon BackColor-S(5), N,-N,N,N,0,N*6,-N, N, -N, -N, 0, -N*6
            \\ LED - BOTTOM
            move XX+N*1.2, YY+N*16.8+NN
            polygon BackColor-S(6), N,-N,N*6,0, N,N, -N, N,-N*6,0, -N, -N
      end sub
}
Led_Clock


 

Σάββατο, 5 Σεπτεμβρίου 2020

Happy Window

This is an example of using graphics in a window layer. First we set Pen to 14 (yellow) and background to 5 (magenta). Also we set to row 0 the split screen, so we have all rows to belong to the lower part, which can scroll (setting to 3 we preserve 3 charater rows above row 3, eg 0, 1 and 2). These statements direct to current layer, the console layer. From another layer we can use Layer { statements here }. The Layer statement may have or not a parameter.  See some code lines below we set the window layer using statements which are common for all layers. After the two first statements Pen and Cls (clear screen) we put a "smooth on" statement to enable the GDI+ (by default is the GDI32 the faster method for drawing).


To declare a form is easy. We can use only a line; declare form1 form. M2000 window manager prepare a window, using a default schema, using Form1 as title and standard width and heihgt. So here we use Layer Form1 { } to change the form to display a graphic face, a happy one;

All coordinates are in twips. Width block get a parameter of pixel unit, so 4 is for 4 pixels.

Step statement use relative positions to move the graphic cursror. Move statement move graphic cursor (hidden always) to absolute positions.

Circle can give ellipse utilizing the ratio (by default is 1 for circle). Also we ca use Circle Fill to fill the circle.

See the Pen numeric_expression { } is a block which change the current layer's pen, and restore it when exit from block.

We make a thread inside the layer. When we make a thread, the threat get a reference to a layer structure, so in each interval 1000/120 (in miliseconds) the layer for this thread switch to windows layer.

We have to erase all threads using Threads Erase, before destroy actuall Window.

We want to hide console and that happen with Title "", 0

Then we we want to open window as modal, using Method Form1, "Show", 1

After we close the window (double clik the square in the title of window) we want to restore the console using Title "A title...here", 1







\\ enable GDI+ for smooth lines for graphics
Pen 14
Cls 5, 0
smooth on
declare Form1 Form
with Form1, "title", "My Happy Window"
layer Form1 {
      Rem Font "Verdana"
      linespace 60
      window 18, 8000,8000;
      Gradient #2255FF, 5
      move scale.x div 2, scale.y div 2
      width 4 {circle Fill #8833aa, scale.x div 2.5}
      width 4 {circle scale.x div 3.5,0.5, pen, pi,0}
      width 4 {circle scale.x div 3.5,0.9, pen, pi,0}
      Pen #33aacc {
            step -scale.x div 5.5, - scale.y div 6.5
            circle fill #33aacc, scale.x div 12.5
            step 2*scale.x div 5.5
            circle fill #33aacc, scale.x div 12.5
      }
      circle fill pen, scale.x div 22.5, 1.5
      step -2*scale.x div 5.5
      circle fill pen, scale.x div 22.5, 1.5
      thread {
            Cursor 0, Height-1
            Print Over $(6),"Current Time: "+str$(now,"long time")
      } as K interval 1000/120
}
\\ hide console
title "", 0
method Form1, "Show", 1
threads Erase
\\ Set a title and restore console
title "M2000 Console", 1
declare Form1 Nothing


Παρασκευή, 4 Σεπτεμβρίου 2020

Converter Metric to and from U.S. (OOP example + GUI example)

First we will see the OOP part. We have a class Converter which make two objects, Metric and US. Because each one need some literals, strings and numeric, we use two stack objects, which each hold a series of data. Using the symbol ! before a stack object we place the items of stack object, not the object to the calling stack. The items from stack objects just moved to the new stack, so d1 and d2 would have zero length hust at the call to class constructor, for each one. We will see a Part 2 which we can use it

Part I



Let d1=Stack, d2=Stack
Stack d1 {
      data "Metric units", 1, 3
      data .01, "Centimeters", "cm", 1, "Meters", "m", 1000,"Kilometers", "km"
}
Stack d2 {
      data "U.S. units", .3048, 4
      data 0.0254,"Inches","in", 0.3048, "Feet", "ft", 0.91441, "Yards", "yd", 1613, "Miles","mi"
}


Class Converter {
private:
      MUnit$, MUsmall$
      Unit=1
      inventory Units
public:
      Final msg$="{0}{1} converted to {2}{3}"
      Property LastUnit${Value}=""
      Function Convert2Meters(V as double, Unit$="Unit", rd=-1) {
            .[LastUnit]$<="m"
            if rd<0 then
                  =V*.Units(Ucase$(Unit$))(0)
            else
                  =Round(V*.Units(Ucase$(Unit$))(0), rd)
            end if
      }
      Function GetSmall$(Unit$="Unit") {
            =.Units$(Ucase$(Unit$))(1)
      }
      Function Convert(Val,Unit1$,To, ToPrint=false) {
            nVal=.Convert2Meters(Val,Unit1$)
            fVal=To(nVal)
            if ToPrint then
                  print Format$(.msg$, Val, .GetSmall$(Unit1$), fVal, To.Unit$)
            end if
            =fVal
      }
      Function ConvertFunc(Unit1$, t) {
            U=.Units(Ucase$(Unit1$))(0)*t(1)
            =lambda U (Val, rd=-1) -> {
                  if rd<0 then
                        =Val*U
                  else
                        =round(Val*U,rd)
                  end if
            }
      }      
      Module ShowConvertion(Val,Unit1$,To) {
            nVal=.Convert2Meters(Val,Unit1$)
            print Format$(.msg$, Val, .GetSmall$(Unit1$), To(nVal), To.Unit$)
      }
      Value (Unit$) {
            Try Ok {
                  f=.Units(Ucase$(Unit$))(0)
            }
            if Error or Not Ok then Error "No such unit:"+Unit$
            if f==0 then Error "Something wrong with unit:"+Unit$
            Group ret {
            Private:
                  factor=f
            Public:
                  Unit$
                  Value (V) {
                        =V/.factor
                  }
            }
            ret.Unit$=.GetSmall$(Unit$)
            =Group(Ret)
      }
Class:
      Module Converter {
            Def uVal as double=1
            read .Name$,.Unit, M
            For i=1 to M
                  read uVal, Unit$, usmall$
                  if uVal==.Unit then
                        .MUnit$<=Unit$
                        .MUsmall$<=usmall$
                        Append .Units,"UNIT":=(.Unit, usmall$)
                  End if
                  Append .Units,Ucase$(Unit$):=(uVal, usmall$)
            Next i
      }
}
Metric=Converter(!d1)
US=Converter(!d2)



Copy this code after part1  to a module Conv1



Inches=Metric.Convert(75,"Centimeters", US("Inches"))
US.ShowConvertion Inches
,"Inches", Metric("Centimeters")
US.ShowConvertion Inches
,"Inches", US("feet")
US.ShowConvertion
100,"Miles", US("feet")
Metric.ShowConvertion
200, "Kilometers", US("Miles")
Metric.ShowConvertion
20, "Kilometers", Metric("Meters")
Miles
=Metric.Convert(500,"Meters", US("Miles"), True)
Print miles;"mi"
Yards
=Metric.Convert(500,"Meters", US("Yards"), True)
Print Yards;"yd"
\\ We can make lambda functions for faster calculation
AnyCentimeterToInches
=Metric.ConvertFunc("Centimeters", US("Inches"))
Print AnyCentimeterToInches(75, 3);"in"  ' 29.528  Inches
Print AnyCentimeterToInches(30, 2);"in"  ' 11.81   ' Inches
Print "Kilometers to Miles"
AnyKilometersToMiles
=Metric.ConvertFunc("Kilometers", US("Miles"))
For i=10 to 180 step 10
      Print i,"km  ->", AnyKilometersToMiles(i, 0),"mi"
next i


The idea is to make a lambda function every time we decide the names of units from one object to other. The converter object return another object (look the value member), which need the name of the unit as a parameter. So US("Miles") returtn an object, but not a converter object. This object also has a value and need a parameter too. Because we want to read another value, we use a group and not a lambda function. Final the ConvertFunc()  function return a lambda function which combine the factors to make a fast multiplication (without using objects).

The last part, the Converter.

Now copy the part one and the code below to a new module say Conv2. Now we place a user interface, and we can insert in two text boxes values, and we can change from dropboxes the units for each system. Also a thread show the time (and refresh the status bar).


Function Local2() is an updated function from info big file (info.gsb), which included in M2000 setup. This used to validate the input string. Validation done without the decimal point. This attatched after this stage. The validation for decimal point happen before the call to Local2() on the ValidString event for each TextBox. A Call Local calls a function as a sub, so we place the same name space (here the Conv2 module is the current name space).


Use Locale 1033 for engilsh or Locale 1032 for greek as first statement. This control the decimal point character, so 1032 set "," (use Monitor statement in M2000 console, to see the state of M2000 Environment).



def SkipValidation1=True, SkipValidation2=True
declare Convertion form
declare TEXTBOX1 TEXTBOX form Convertion
declare COMBO1 COMBOBOX form Convertion
declare TEXTBOX2 TEXTBOX form Convertion
declare COMBO2 COMBOBOX form Convertion
layer Convertion {
      LineSpace 60
      window 10, 12000, 5000;
      Cls 1, 0
      gradient #aa9933,1
      totalwidth=scale.x
}
boxwidth
=totalwidth-2000
method TEXTBOX1, "MOVE", 1000, 1000, boxwidth
method COMBO1, "MOVE", 1000, 1800, boxwidth
method TEXTBOX2, "MOVE", 1000, 2600, boxwidth
method COMBO2, "MOVE", 1000, 3400, boxwidth
with Convertion, "TITLE", "M2000 Example: Converter "
\\ ADJUST PROPERTIES FOR CONTROLS
\\ FROM SOME PROPERTIES  WE WANT SOME IDENTIFIERS TO BOUND TO
with COMBO1, "EDIT", false,"UseOnlyTheList", true,"ShowAlways", false
with COMBO1, "ListText" as ITEM1$, "Text" as COMBO1$, "label","Units:"
ITEM1$
={Centimeters
            Meters
            Kilometers
            }
COMBO1$
="Meters"
with COMBO2, "EDIT", false,"UseOnlyTheList", true,"ShowAlways", false
with COMBO2, "ListText" as ITEM2$, "Text" as COMBO2$, "label","Units:"
ITEM2$
={Inches
            Feet
            Yards
            Miles
            }
COMBO2$
="Yards"
with TEXTBOX1, "Prompt", "Metric: ", "VarText" as textbox1.value$
with TEXTBOX1, "ThisKind" as TK1$,"ShowAlways", true
TK1$
=" "+Metric.GetSmall$(COMBO1$)
textbox1.value$
="0"
with TEXTBOX2, "Prompt", "U.S.: ", "VarText" as textbox2.value$
with TEXTBOX2, "ThisKind" as TK2$,"ShowAlways", true
TK2$
=" "+US.GetSmall$(COMBO2$)
textbox2.value$
="0"
const fstr$="{0} <=> {1}"
caption$
=format$(fstr$, combo1$, combo2$)
FromText1
=Metric.ConvertFunc(Combo1$, US(combo2$))
FromText2
=US.ConvertFunc(Combo2$, Metric(combo1$))
Function Local2(new Feed$, z) {
      \\ this Function can be used from other Integer
      \\ this$ and thispos, exist just before the call of this Function
      local sgn$
      if feed$="" and this$="-" then thispos-- : exit
      if left$(this$,1)="-" then sgn$="-": this$=mid$(this$, 2)
      if this$<>Trim$(this$) then this$=Feed$ : thispos-- : exit
      if Trim$(this$)="" then this$="0" : thispos=2 : exit
      if instr(this$,"+")>0 and sgn$="-" then this$=filter$(this$, "+") : sgn$=""
      if instr(this$,"-")>0 and sgn$="" then this$=filter$(this$, "-") : sgn$="-"
      if filter$(this$,"0123456789")<>"" then this$=Feed$ : thispos-- : exit
      if len(this$)>1 then While left$(this$,1)="0" {this$=mid$(this$, 2)}
      this$=sgn$+this$
      if Z<>0 then FEED$=this$: INSERT Z FEED$=de$: this$=FEED$: thispos++
      if this$="-0" then this$="-" : thispos=2
      }
Function 
TextBox1.ValidString {
      \\ this Function called direct from textbox
      read new &this$, &thispos
      if SkipValidation1 then exit function
      local de$=locale$(14)
      this$=replace$(de$, ".", this$)
      local Z=INSTR(this$,"."), K$
      if Z>0 then
            K$=FILTER$(this$, ".")
            if LEN(K$)+2<LEN(textbox1.value$) then
                  this$=textbox1.value$
                  this$=replace$(de$, ".", this$)
                  Z=INSTR(this$, ".")
                  K$=FILTER$(this$, ".")
            END if
            if z>0 then thispos--
            this$=K$
      End if
      call local local2(textbox1.value$, Z)
}
Function 
TextBox2.ValidString {
      \\ this Function called direct from textbox
      read new &this$, &thispos
      if SkipValidation2 then exit function
      local de$=locale$(14)
      this$=replace$(de$, ".", this$)
      local Z=INSTR(this$, "."), K$
      if Z>0 then
            K$=FILTER$(this$, ".")
            if LEN(K$)+2<LEN(textbox2.value$) then
                  this$=textbox2.value$
                  this$=replace$(de$, ".", this$)
                  Z=INSTR(this$, de$)
                  K$=FILTER$(this$, de$)
            END if
            if z>0 then thispos--
            this$=K$
      End if
      call local local2(textbox2.value$, Z)
}
Function Combo1.dblclick {
      caption$=format$(fstr$,combo1$, combo2$)
      FromText1=Metric.ConvertFunc(Combo1$, US(combo2$))
      FromText2=US.ConvertFunc(Combo2$, Metric(combo1$))
      call local TextBox2.Enter()
      TK1$=" "+Metric.GetSmall$(COMBO1$)
      method Convertion, "refreshall"
}
Function Combo2.dblclick {
      caption$=format$(fstr$, combo1$, combo2$)
      FromText1=Metric.ConvertFunc(Combo1$, US(combo2$))
      FromText2=US.ConvertFunc(Combo2$, Metric(combo1$))
      call local TextBox1.Enter()
      TK2$=" "+US.GetSmall$(COMBO2$)
      method Convertion, "refreshall"
}
Function TextBox1.Enter {
      SkipValidation2~
      textbox2.value$=str$(Fromtext1(val(textbox1.value$, locale$(14))),"")
      SkipValidation2~
      clipboard textbox2.value$+" "+combo2$
}
Function TextBox2.Enter {
      SkipValidation1~
      textbox1.value$=str$(Fromtext2(val(textbox2.value$, locale$(14))),"")
      SkipValidation1~
      clipboard textbox1.value$+" "+combo1$
}
SkipValidation1
=False
SkipValidation2
=False
layer Convertion {
      thread {
            cursor 0, Height-1
            print over $(7, width),~(#FFFFFF), Caption$+" | "+str$(now,"long time")
            print part $(4, width),~(#FFFFFF), "George Karras 2020"
      } as T1 interval 1000/60
}
method Convertion, "SHOW", 1
threads Erase
declare Convertion nothing


George Karras