Alternative zu str(int(float(self.feedrate)))

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
hirnwunde
User
Beiträge: 15
Registriert: Mittwoch 3. September 2014, 09:41

Hallo Leute,

ich möchte einen Wert, der mitunter als "20.53" daherkommt auf int wandeln
und ihn spaeter in einen zusammengestzten String ausgeben.

Da ich bei der Zusammensetzung eines Strins mittels "," immer ein Leerzeichen
von Python spendiert bekomme, habe ich mich fuer das "+" entschieden.
Denn da gibt es kein zusaetzliches Leerzeichen.

Klar, dass Python keine Zahl auf einen String addieren kann.
Also aus dem Int, was vorher ein Float war, einen String machen.

Gibt es eine Alternative zu dem hier?

Code: Alles auswählen

str(int(float(self.feedrate)))
Danke!

Falls der komplette Code von Belang ist:

Code: Alles auswählen

teststring1 = "G1 Y20.322 F12.1 X300"
teststring2 = "G01 X20.322 Y12.1 I30 J35 F300"

class translate(): 
    
    GCommandNotSupported = False
    MCommandNotSupported = False
    supportedGCommands = ["G01", "G1", "G04", "G4", "G28", "G90", "G91", "G92", "G999"]
    supportedMCommands = ["M18", "M100", "M114"]
    coord = [0.0, 0.0]
    feedrate = 0
    
    def getCleanedCode(self, line):
        
        splitted = line.split(" ")

        cmd = splitted[0]

        if cmd.startswith("G"):

            if cmd in self.supportedGCommands:

                if splitted[1]:
                    firstval = splitted[1]
                    if firstval.startswith("X"): self.coord[0] = firstval[1:]
                    if firstval.startswith("Y"): self.coord[1] = firstval[1:]
                    if firstval.startswith("F"): self.feedrate = firstval[1:]

                    if splitted[2]:
                        secondval = splitted[2]
                        if secondval.startswith("X"): self.coord[0] = secondval[1:]
                        if secondval.startswith("Y"): self.coord[1] = secondval[1:]
                        if secondval.startswith("F"): self.feedrate = secondval[1:]

                        if splitted[3]:
                            thirdval = splitted[3]
                            if thirdval.startswith("X"): self.coord[0] = thirdval[1:]
                            if thirdval.startswith("Y"): self.coord[1] = thirdval[1:]
                            if thirdval.startswith("F"): self.feedrate = thirdval[1:]


                    rndcoord = self.getRounded(self.coord)

                    
                    print 'DBG X' + str(rndcoord[0]) + ' Y' + str(rndcoord[1]) + ' F' + str(int(float(self.feedrate)))


            else:
                self.GCommandNotSupported = True
                print "unsupported G"
            
        elif cmd.startswith("M"):
            print "MCommand"
            if cmd in self.supportedMCommands:
                print "supported M"
            else:
                self.MCommandNotSupported = True
                print "unsupported M"
            
        else:
            # error-routine
            pass
            print "Unsupported Command. Only G- or M-Commands accepted!"
    
    def getRounded(self, __coord):
        
        xcoord = format(round(float(__coord[0]), 2), '.2f')
        ycoord = format(round(float(__coord[1]), 2), '.2f')
        
        rndcoord = [xcoord, ycoord]
        return rndcoord

a = translate()

a.getCleanedCode(teststring1)
a.getCleanedCode(teststring2)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@hirnwunde: Du mußt zwischen interner Darstellung und dem Import und Export von Daten klarer trennen. Intern sind alles Zahlen und sollten daher auch als Zahlen gespeichert werden und nicht als Strings.
Dazu wäre es praktisch, wenn Du einen Parser schreibst, der einen String in ein internes Format umwandeln kann. Etwa so:

Code: Alles auswählen

def parse_line(line):
    parts = line.split()
    return parts[0], dict((param[0],float(param[1:])) for param in parts[1:])

cmd, params = parse_line("G01 X20.322 Y12.1 I30 J35 F300")
Zur Ausgabe empfiehlt es sich Stringformatierung zu benutzen. Dabei kann mit dem Formatstring '.0f' auch ein float ohne Nachkommastellen ausgegeben werden:

Code: Alles auswählen

print "DBG X{X:.2f} Y{Y:.2f} F{F:.0f}".format(**params)
PS: Deine Klasse ist nicht wirklich eine. Das wäre alles besser in einer Funktion aufgehoben.
hirnwunde
User
Beiträge: 15
Registriert: Mittwoch 3. September 2014, 09:41

Danke Sirius,

dann werde ich mir mal einen Parser basteln.

Das mit der Klasse ist eher ein Lern-Produkt fuer mich selbst.
Urspruenglich wollte ich die Klasse spaeter in ein Modul auslagern.

Oder kann ich klassenlose Funktionen via import einbinden?

Happy hacking
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

hirnwunde hat geschrieben: Oder kann ich klassenlose Funktionen via import einbinden?
Selbstverständlich! Du kannst auch beliebige Objekte via Import einbinden ;-)

Du solltest Dich bei der Namensgebung an PEP8 halten.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
hirnwunde
User
Beiträge: 15
Registriert: Mittwoch 3. September 2014, 09:41

Hi Leute

Danke Hyperion fuer die Hinweise. Ich habe das PEP8 angefangen zu lesen
und waerend des lesens kamen mir ein paar Ideen, die mich "zwangen" mit
dem Lesen aufzuhoeren :-)
Ich werde das Dokument sicher noch intensiver studieren ...

Sirius' Rat befolgend, habe ich mir einen "parser" gebastelt. (der hielt mich vom lesen ab ;-))
Allerdings ist der etwas umfangreicher ausgefallen als sein Drei-Zeiler,
weil ich mir dachte, warum nicht gleich alles in dieser Funktion erledigen.

Code: Alles auswählen

import re
import string

teststring1 = "G01 y20.322 F12.1 X300"
teststring2 = "G1 X20.322 Y12.1947 I30 J35 F300"
def paramlist_to_dictionary(paramlist):
    
    paramdict = {}
    laenge = len(paramlist)    
    paramdict.update({paramlist[1]: paramlist[2]})
    if laenge > 3: paramdict.update({paramlist[3]: paramlist[4]})
    if laenge > 5: paramdict.update({paramlist[5]: paramlist[6]})
    if laenge > 7: paramdict.update({paramlist[7]: paramlist[8]})
    if laenge > 9: paramdict.update({paramlist[9]: paramlist[10]})
    if laenge > 11: paramdict.update({paramlist[11]: paramlist[12]})
    
    for key, value in paramdict.iteritems():
        if key == "G": paramdict["G"] = int(value)
        if key == "M": paramdict["M"] = int(value)
        if key == "F": paramdict["F"] = int(float(value))
        if key == "X": paramdict["X"] = float(format(round(float(value), 2), '.2f'))
        if key == "Y": paramdict["Y"] = float(format(round(float(value), 2), '.2f'))
    
    return paramdict

pdict = paramlist_to_dictionary(re.split(r'(G|X|Y|I|J|F)', string.upper(teststring2).replace(" ", "")))

print "G|", pdict["G"], type(pdict["G"])
print "X|", pdict["X"], type(pdict["X"])
print "Y|", pdict["Y"], type(pdict["Y"])
print "F|", pdict["F"], type(pdict["F"])
Etwas umfangreicher ...
Das 'umcasten' in Zeilen 21 und 22 ist meine einzig bekannte Moeglichkeit, eine
Float-Zahl mit zwei Nachkommastellen ausgeben zu koennen, wenn beim
runden der Ausgangszahl 13.1 heraus kommt. Ich moechte aber 13.10 ...

Was fuer Boecke hab ich denn jetzt so geschossen? (abgesehen von den (noch) nicht beachteten PEP8-Guidelines :-))
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@hirnwunde: Wenn Du eine Zeile mehrfach kopierst, willst Du eigentlich eine Schleife benutzen. Für einen einzelnen Key ist update quatsch, dafür gibt es den Indexzugriff. Der Parser sollte schon alles machen und nicht auch noch Teile im Hauptprogramm. Runden solltest Du erst bei der Ausgabe, siehe meinen vorherigen Post.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hui... da ist vieles.. unschön!

Zum einen dieses:

Code: Alles auswählen

paramdict.update({paramlist[1]: paramlist[2]})
Das ist der *umständliche* Weg folgendes zu sagen:

Code: Alles auswählen

paramdict[paramlist[1]] = paramlist[2]
;-)

Wenn Du schon Regular Expressions nutzt, wieso extrahierst Du Dir nicht sofort "Key-Value-Pairs"?

Code: Alles auswählen

re.findall(r"([GXYIJF])(\d+.?\d*)", s)
>
[('G', '1 '),
 ('X', '20.322'),
 ('Y', '12.1947'),
 ('I', '30 '),
 ('J', '35 '),
 ('F', '300')]
Daraus kann man trivialer Weise ein Dictionary machen.

Und wenn man das hat, kann man die Umformungen noch kompakter gestalten:

Code: Alles auswählen

xy = lambda v : round(float(v), 2)
transformations = {'G': int, 'M': int, 'F': lambda v: int(float(v)), 'X': xy, 'Y': xy}
Damit kann man sich dann in einer Schleife alle Werte umformen:

Code: Alles auswählen

for key, value in tokens.items():
    print(key, transformations.get(key, lambda v: v)(value))
>
J 35 
Y 12.19
F 300
X 20.32
I 30 
G 1
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Da kannst du noch so einiges verbessern. Ich fange einfach mal von oben nach unten an.

Hättest du PEP 8 bis zum Ende gelesen, dann würden Zeilen 4 und 5 nicht so aussehen ;-)

"paramdict" und "paramlist" sind schlecht gewählte Namen. Datentypen sollten nicht in Namen stehen. Typen ändern sich schnell, dann müsstest du auch überall die Namen anpassen.

Zeile 10 ist irgendwie unnötig. Du kannst in Zeile 8 gleich das Dictionary initialisieren. Ein update mit nur einem Item ist außerdem sehr ungewöhnlich, da kannst du auch direkt eine Zuweisung benutzen.

Zeilen 11 bis 15 sind quasi identisch, das lässt sich mit einer Schleife viel einfacher lösen. Auch hier hast du wieder update-Aufrufe, welche ganz normale Zuweisungen sein sollten.

Zu Zeilen 18 bis 22: "elif" existiert und sollte von dir benutzt werden ;-) Die Zeilen sind auch wieder fast alle identisch und lassen sich zusammenfassen. Tipp: Funktionen sind in Python auch Objekte und können an Namen gebunden werden.

Bei Zeilen 21 und 22 sieht man gut, dass du irgendwie herumgeraten hast. Dir scheint der Unterschied von interner Darstellung und Ausgabe nicht klar zu sein. Wenn du nur zwei Nachkommastellen darstellen möchtest, dann mache das bei der Darstellung, aber runde nicht vorher.

Der Ganze Code ab Zeile 26 sollte in eine main-Funktion gepackt werden, sonst kann man dein Modul nicht sinnvoll wiederverwenden. In Python gibt es dafür ein Idiom:

Code: Alles auswählen

def main():
    ...

if __name__ == "__main__":
    main()
"pdict" ist wieder ein nichtssagender Name und der Import des string-Moduls ist überflüssigs, Strings haben eine upper-Methode. Auch sollten Zeilen nicht länger als 80 Zeichen sein.
Das Leben ist wie ein Tennisball.
hirnwunde
User
Beiträge: 15
Registriert: Mittwoch 3. September 2014, 09:41

Uff ... viel Erklaerungsbedarf meinerseits :)

Da sich hier in mindestens zwei Posts auf den Aufbau des Codes als solches beziehen:

Allem Anschein nach sind meine Wege, Funktionen zu schreiben/testen andere, als sie
in der professionellen Programierung bestritten werden.
Ich bin ein Bauschmied, der sich Programmiersprachen nach dem Prinzip Problem -> Loesung
"beibrachte", ohne eine Sprache wirklich im Detail zu kennen.
Das dort bestimmte Sachen bescheuert geloest sind, ist mir bewusst.

Ab Zeile 28 ging es mir nur darum, die Ergebnisse auszugeben, um zu schauen, ob
das, was ich da mache ueberhaupt funktioniert.
Zeile 6-24 werden spaeter ausquartiert und nicht Teil der main-Funktion.
In Eclipse finde ich das hin- und herwechseln zwichen den Tabs, weil ich jedes mal mit
zur Maus greifen muss und [Alt]-[Pfeiltasten] nicht immer da landet, wo es soll.
Deswegen steht der einfacheit halber alles in einer Datei und wird dann ausgelagert.

Ich bin mir bewusst, dass dies sicher nicht der cleverste Weg ist.
Das unterscheide mich halt von einem Programmierer, der seinen Lebensunterhalt
damit bestreitet.

Nicht, dass ich nicht bereit bin, neue Wege zu lernen. Anderenfalls waere ich wohl kaum
hier gelandet ;)
Aber mir bereitet es gerade schon ausreichend Mühe, ueberhaupt erstmal die Grundzuege Python's zu lernen.

Nicht dass ihr mich falsch versteht! Ich bin euch allen sehr dankbar!
Eurer Enthusiasmus ist bemerkenswert und durch nichts zu bezahlen.
Nichts was ihr mir (und den anderen Interessierten) schreibt, bleibt verloren. Es wird mir
spaeter alles wieder einfallen, wo ich nachzusehen hab, wenn ich etwas richtig machen soll.
Aber viele Ratschlaege, die ihr mir erteilt, verstehe ich einfach nicht.
Und wenn ich die Wahl habe zwischen Code, den ich verstehe, der aber unperformant oder haesslich
ist zwischen schoenem, performanten aber unverstaendlichen Code ...
Ich arbeite am Verstaendnis!

Soo ... entschuldigt .. BTT!

@Sirius bzgl. Schleife, dict.update() und round() aus der Funktion: Wird geaendert. Danke.

@Hyperion:
dict.update wurde ja schon von Sirius angesprochen.

Zum Thema RegExp und wieso ich mir keine "Key-Value-Pairs" extrahiere.
Eigentlich eine ganz einfache Antwort. Weil ich so gut wie keinen Plan von REs habe.
Meine letzten Beruehrungen dahingehend sind ca. 15 Jahre her ... damals noch mit sed auf der Shell.
Und das war auch nicht der Rede wert. REs sind einfach nochmal eine Sprache. Ich wollte es einfach halten
und mich nicht mit noch mehr Ballast bewerfen.

Was Du dort in zwei Zeilen gezaubert hast ist einfach weit ueber meinem Niveau und Verstaendnis.
_DAS_ fuehrt meine komplette Funktion ad absurdum

@EyDu:
' anstatt "
Ist in meinem Kopf zwar angekommen, aber es ist die Macht der Gewohnheit immer die doppelten
Anfuehrungszeichen zu nutzen ...

Ebenso bzgl. der Ungarischen Notation ... auch das wurde mir vor kurzen versucht auszutreiben.
Aber wie ich schon eingangs erwaehnte. Ich habe bisher noch Probleme bei den simpelsten Dingen
mit Python, da vergesse/verdraenge ich gerne mal, mir sinnvollere Bezeichner auszudenken.

Alles weitere wurde ja schon weiter oben angemerkt. Danke dennoch fuer die Hinweise.

Danke euch Allen!!
Und entschuldigt diesen Monster-Post, der kurz vor halb zwei mal nicht mehr korrektur gelesen wird. ;-)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@hirnwunde: Verbesserungsvorschläge lassen sich immer in zwei Kategorien einteilen. Solche, die komplexere Konstrukte vorschlagen um Probleme sauberer zu lösen, und andere, die einfach nur eine Einstellungssache sind. Man kann sich Sachen falsch angewöhnen und hat dann später Probleme, sie wieder aus dem Kopf zu bekommen, oder man macht sie gleich richtig, ohne dass irgendetwas viel komplizierter wird. Namenskonventionen sind so ein Beispiel: Auf der einen Seite ist nichtssagender Buchstabensalat, der eine ungewöhliche Groß-Klein-Schreibung hat, auf der anderen aussagekräftige Namen, die sich an die drei Regeln hält, die in PEP8 stehen. Und niemand kann behaupten, dass drei Regeln sich nicht merken und anwenden lassen (die ersten drei Stunden bewußt, danach unbewußt). Genauso verhält es sich mit Syntax-Konstrukten, die zu kompliziert sind ("update") und durch etwas viel einfacheres ersetzen lassen. Das falsche sich zu merken ist genauso ein großer Aufwand wie die richtige Lösung.
Übrigens: " und ' sind absolut gleichwertig, da behauptet EyDu auch nichts anderes.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

hirnwunde hat geschrieben: Zum Thema RegExp und wieso ich mir keine "Key-Value-Pairs" extrahiere.
Eigentlich eine ganz einfache Antwort. Weil ich so gut wie keinen Plan von REs habe.
Meine letzten Beruehrungen dahingehend sind ca. 15 Jahre her ... damals noch mit sed auf der Shell.
Und das war auch nicht der Rede wert. REs sind einfach nochmal eine Sprache. Ich wollte es einfach halten
und mich nicht mit noch mehr Ballast bewerfen.
Nicht schlimm - ich bin hier auch nicht grad der Regex-Gott ;-) (Die Whitespaces am Schluss müssten eigentlich auch noch weg!)
hirnwunde hat geschrieben: Was Du dort in zwei Zeilen gezaubert hast ist einfach weit ueber meinem Niveau und Verstaendnis.
_DAS_ fuehrt meine komplette Funktion ad absurdum
Auch das ist nichts "magisches" an sich. Ich habe lediglich den Code von Dir zwischen den Zeilen 18 und 22 ein wenig vereinfacht, indem ich die an die Schlüssel geknüpften Umwandlungsfunktionen zusammen in eine Datenstruktur gepackt habe und damit direkt eine Umformung vom jeweiligen Wert mit der zugehörigen Umwandlungsfunktion durchführen kann. Du hast vermutlich noch keine ``lambda``-Ausdrücke kennengelernt und bist darüber hinaus überrascht, dass man Funktionen wie andere Werte bzw. Objekte behandeln kann :-) Kann man! Und dabei ist nichts letztlich nichts magisches; man muss es nur wissen, akzeptieren und sich daran "gewöhnen" :-)

Einiges dazu kannst Du in einem Tutorial aufschnappen, was ich vor einiger Zeit mal geschrieben habe. Hat zwar thematisch einen anderen Inhalt, aber genau diese Prinzipien werden darin auch besprochen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@hirnwunde: Und noch ein Ansatz:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
from future_builtins import map

PARAMETERNAME_TO_CONVERT = {
    'F': float,
    'M': int,
    'X': float,
    'Y': float,
}

SUPPORTED_COMMANDS = set(
    [
        'G01', 'G1', 'G04', 'G4', 'G28', 'G90', 'G91', 'G92', 'G999',
        'M18', 'M100', 'M114',
    ]
)


def parse_argument(string):
    name = string[0]
    return (name, PARAMETERNAME_TO_CONVERT.get(name, lambda x: x)(string[1:]))


def argument_to_string((name, value)):
    return '{0}{1}'.format(
        name,
        format(
            value, '.2f' if PARAMETERNAME_TO_CONVERT.get(name) is float else ''
        )
    )


class Command(object):
    def __init__(self, command, arguments, is_supported=True):
        self.command = command
        self.arguments = list(arguments)
        self.is_supported = is_supported

    def __str__(self):
        return '{0} {1}'.format(
            self.command, ' '.join(map(argument_to_string, self.arguments))
        )

    @classmethod
    def parse(cls, line):
        command, _, arguments = line.upper().partition(' ')
        return cls(
            command,
            map(parse_argument, arguments.split()),
            command in SUPPORTED_COMMANDS
        )


class Program(object):
    def __init__(self, commands):
        self.commands = list(commands)

    def __str__(self):
        return '\n'.join(map(str, self.commands))

    @classmethod
    def parse(cls, lines):
        return cls(map(Command.parse, lines))


def main():
    lines = ['G1 y20.322 F12.1 X300', 'G01 X20.322 Y12.1 I30 J35 F300']
    program = Program.parse(lines)
    print(program)


if __name__ == '__main__':
    main()
Das/die Kommandos habe ich unverarbeitet gelassen weil ich dachte das 'G04' und 'G4' unterscheidbar sein müssen. Nachdem ich jetzt auf Wikipedia nach „G-Code” geschaut habe bin ich mir sehr unsicher ob das überhaupt so Zeilenweise funktioniert wie Dein Ursprungscode den Anschein macht oder ob man nicht grundsätzlich auch alles in eine Zeile schreiben kann und 'G's und 'M's auch irgendwo mitten in einer Zeile stehen können.
hirnwunde
User
Beiträge: 15
Registriert: Mittwoch 3. September 2014, 09:41

Das groesste Problem, was ich bei euren Code-Beispielen/Loesungen habe ist
der Umstand, das ich sie kaum verstehe.

Ich werde wohl in absehbarer Zeit nie wirklich "sauberen Code" schreiben,
weil ich zu wenig Zeit habe, um das alles, was ihr mir hier empfehlt, auch
wirklich umzusetzen.

Beziehungsweise ... anders gesagt.
Die Zeit werde ich mir nehmen. Es wird aber sehr lange dauern.
Ich habe drei Kinder (2, 8 und 9) und auch die wollen beschaeftigt werden.
Da investiere ich lieber Zeit in das Lehren von Scratch und das basteln von
Arduino-DrueckMich-IchPiepseUndBrumme-Sachen (fuer die Kleinste).
Meine Kids werden mir dann konformes Python lehren ;)

Vielleicht (wohl eher Augenscheinlich) ist der Ansatz, wie ich ihn bisher fuhr, grundlegend falsch.
Sprache lernen durch:
- Ich habe ein Problem
- waelze Referenzen/SO um mir die geeigneten Methoden/Funktionen zu suchen
- loese das Problem mit der/den von mir verstandenen Methoden/Funktionen
(ungeachtet dessen, dass es weitaus bessere Loesungswege gibt)

Ich glaube, Python ist dafuer einfach zu hoch. Oder einfach fuer mich ;)
Ohne grundlegende Kenntnisse, die einem im Studium vermittelt werden, wird man hier nicht
weit kommen. Mir fehlt die Denke eines Informatikers.

So, wie ich in meiner Lehrzeit lernen musste, wie man richtig einen Hammer haellt, wie man
mit Elektroden schweisst oder wie man einen Meissel haertet.

Und ich will ebensowenig, das ich hier meinen "dreckigen" Code poste und ihr diesen
sauber macht und ich diesen dann 1:1 uebernehme, ohne ihn zu verstehen.
Waere zwar bequem, aber unfair euch gegenueber.
Und verstanden haette ich dadurch auch nur in Bruchteilen etwas.

Ich warte jetzt auf "Einfuehrung in Python" von O'Reilly ... vielleicht bringt dieses ja
Erhellung bei mir und ich verstehe dann mehr als vorher.

@BlackJack

G-Code ist eine sehr einfache Sprache :)
G01/G1 und G03/G3 sind gleich.
Es gibt CAM-Systeme (das sind Programme, die CAD-Daten in "CNC-Sprache" wandeln),
die geben G1, andere wieder G01 aus.
Ein G-oder M-Befehl sollte (bis auf wenige Ausnahmen) immer an erster Stelle stehen.

Aber ein "F" ist m.W. immer ein Int. Das beschreibt die "feedrate", als die Beschleunigung, die
die Achse macht. Wahlweise in Umdrehung/mm oder mm/[Minute|Sekunde].
Mir ist bisher keine CNC-Maschine untergekomen, die da mit einer Fliesskommazahl arbeitet.

Zumindest mein Arduino-CNC-Plotter kann bei dieser Angabe nur mit einer Ganzzahl umgehen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

hirnwunde hat geschrieben:Ich werde wohl in absehbarer Zeit nie wirklich "sauberen Code" schreiben,
weil ich zu wenig Zeit habe, um das alles, was ihr mir hier empfehlt, auch
wirklich umzusetzen.
Es erwartet auch keiner von dir, dass du sofort sauberen Code schreibst, du solltest aber zumindest probieren. Wir geben dir die Hinweise nicht, weil der Code uns dann einfach besser gefällt, sondern weil es vieles einfacher macht. Für dich und für uns. Du hast weniger zu schreiben und wir können deinen Code lesen. Letzteres ist natürlich wichtig, wenn du häufiger Fragen hast. Wenn du konsequent Hinweise ignorierst, dann wird irgendwann keiner mehr helfen. Wobei ich bei deinen Ausführungen da eigentlich keine Bedenken habe.
hirnwunde hat geschrieben:Vielleicht (wohl eher Augenscheinlich) ist der Ansatz, wie ich ihn bisher fuhr, grundlegend falsch.
Sprache lernen durch:
- Ich habe ein Problem
- waelze Referenzen/SO um mir die geeigneten Methoden/Funktionen zu suchen
- loese das Problem mit der/den von mir verstandenen Methoden/Funktionen
(ungeachtet dessen, dass es weitaus bessere Loesungswege gibt)
Das ist keine so gute Strategie. Du musst erst die Grundlagen lernen und anschließend kannst du mit deinem Ansatz fortfahren. Klar, du musst erstmal ein paar Tage investieren, aber danach bist du deutlich effizienter. Die aufgebrachte Zeit holst du locker wieder raus. Für den Einstieg lohnt sich das offizielle Tutorial, welches es auch als deutsche Übersetzung gibt.

Was ich gestern noch vergessen habe zu erwähnen: Bitte höre auf in deinen Postings überall Zeilenumbrüche einzubauen. Das mag zwar bei dir im Textfeld schön aussehen, führt bei allen anderen aber schrecklicher Formatierung. Auf einem großen Bildschirm bestehen deine Beiträge bei mir zu 2/3 aus leerem raum, auf einem kleinen Display ist dein Text kaum vernünftig lesbar. Lass doch bitte jeden Benutzer selbst über die Darstellung entscheiden und übernehme das nicht ;-)
Das Leben ist wie ein Tennisball.
BlackJack

@hirnwunde: Ein Problem bei dem Ansatz sehe ich darin das vor oder nach dem ersten Punkt, „Ich habe ein Problem”, einen Lernen der Grundlagen der Programmiersprache fehlt. Syntax, Kontrollstrukturen, die Grunddatentypen, Ausnahmen, definieren von eigenen Datentypen/Klassen, sind ja teilweise Voraussetzung um die Sachen die man in Referenzen oder auf SO findet auch verstehen und anwenden zu können. Oder auch die Quelltexte die hier so vorgeschlagen wurden. Dazu braucht man kein Informatikstudium.

Bei der Umsetzung wirken Deine Beispiele ein wenig chaotisch was das Vorgehen und die Aufteilung angeht. Das könnte aber auch einfach daran liegen das Python eine neue Sprache für Dich ist und einiges, wenn nicht alles, durch ausprobieren entstanden ist. Und zumindest mir ist die Eingabe mit der gearbeitet wird nicht ganz klar, weil ich G-Code nicht kenne, und auch das Ziel nicht wirklich. Was soll denn tatsächlich letztendlich mit dem G-Code Quelltext gemacht werden? Die Klasse heisst `translate` und die Methode/Funktion `getCleanedCode()`. Also soll das Programm in irgendeine andere Form/Repräsentation übersetzt werden, oder der Quelltext nach irgendwelchen Regeln verändert werden, aber G-Code bleiben?

G-Code mag generell eine einfache Sprache sein, aber man begeht bei so etwas gerne den Fehler etwas einfacher zu sehen als es ist. Auf den ersten Blick auf Deinen Quelltext dachte ich das ist ein 'M' oder 'G' Befehl pro Zeile und so eine Zeile besteht aus eine Folge von Buchstaben denen direkt eine Zahl folgt, getrennt durch Leerzeichen. Mittlerweile weiss ich aber das so ein Programm auch mit '%' anfangen und aufhören kann, dass es zwei verschiedene Arten von Kommentaren geben kann, das 'M' und 'G' nicht am Anfang stehen müssen und es auch mehrere solcher Kommandos in einer Zeile geben kann, und das nicht einmal das Buchstabe+Zahl-Muster alles ist, sondern dass man auch benannte Subroutinen und Kommandos aus mehr als einem Buchstaben wie 'call' haben kann, um solche Subroutinen aufzurufen. Dann sieht die Sprache dann plötzlich nicht mehr so einfach aus, und Dein und auch unser aller Quelltext hier kommt nur mit einem kleinen Bruchteil dieser Möglichkeiten klar.

Bevor man da also drauf los programmiert sollte man vielleicht erst einmal spezifizieren wie eine mögliche Eingabe aussehen darf, also welche Untermenge von G-Code verarbeitet werden kann und welche weiteren Einschränkungen dabei gelten (sollen). Das 'M' und 'G' am Zeilenanfang stehen müssen halte ich zum Beispiel eher für eine Konvention und hätte grundsätzlich erst einmal Zweifel das sich da zum Beispiel Programme dran halten die automatisch G-Code aus anderen Formaten erzeugen.

Und die nächste Frage wäre was das Programm mit so einem Quelltext alles machen können soll, damit man weiss in welche Datenstrukturen man das herunterbrechen muss.

Zum 'F': In Deinem Beispiel kommt ein 'F12.1' vor und in Beispielen im Netz habe ich das auch gesehen. Zum Beispiel bei Wikipedia wo ein 'F.05' im Programmbeispiel steht. Da es auch Kommandonummern mit Dezimalpunkt gibt, habe ich fast den Eindruck dass generell Dezimalbrüche hinter den Einzelbuchstaben vorgesehen sind/waren.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Vielleicht gibt es auch schon Projekte oder Python-Module, die so etwas können?

Durch pypi.python.org und etwas googlen habe ich auf die schnelle folgendes gefunden:

- http://sourceforge.net/projects/pycam/
- http://replicat.org/generators
- http://wiki.linuxcnc.org/cgi-bin/wiki.p ... Generators

K.A. ob da etwas dabei ist oder man Teile davon nutzen oder als Inspiration nehmen könnte :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
hirnwunde
User
Beiträge: 15
Registriert: Mittwoch 3. September 2014, 09:41

Bzgl der Zeilenumbrueche in meinen Posts.

Geht es nur mir so, oder sind Texte, die ueber den ganzen Bildschirm gehen (bei 1920px Breite) weitaus unleserlicher als schmale Einrueckung?
Wenn ich mit den Augen erstmal wieder 50cm nach links muss habe ich Probleme, die naechste Zeile zu finden ... abr gut: das mag meine Alter zuzuschreiben sein. Ich lass ab sofort (hier) die Finger von haeufigeren '\n's :)

Das Tutorial habe ich erstmal ausser acht gelassen, da es fuer Python3 vorgesehen ist, ich aber 2.7 nutze. Mir war der Umstand, dass es fuer 2.7 mehr Module als fuer 3.x gibt ausschlaggebend dafuer, 2.7 zu nutzen. Ungeachtet dessen, ob ich ueberhaupt die Masse an Modulen benoetige. Ich habe nicht ausreichend darueber nachgedacht!

Bisher bin ich mit der "Lern"-Strategie ganz gut gefahren. Aber das war wohl eher mein eigenes Empfinden. Denn ein Grossteil der Arbeiten werden/wurden nur intern genutzt und ich bin der einzige hier, der "programmiert" ;)

@BlackJack

Bezueglich des chaotischen Codes gebe ich dir voll und ganz Recht. Das sind Ergebnisse aus Try&Error ...

Was soll mit dem G-Code passieren?

Ich moechte alle Befehle, die mein Plotter nicht versteht, herausfiltern. (und dem Benutzer spaeter einen Hinweis darauf geben)
Der Umfang der G-Code-Spezifikation ist riesig. Da es jedem Hersteller freigestellt ist, eigene G-Codes zu definieren. Meine Deckel-Maho (von Anno dazumal) deckt die herstellerunabhaengigen ~25 G-Befehle ab, hat aber noch ca. 70 herstellereigene Befehle. Es kann sein, das es Schnittmengen der Befehle auf einer Fanuc (ein anderer CNC-Maschinen-Hersteller) gibt. Aber das ist eben nicht "Vorschrift".
Mein Plotter kann hingegen nur 8 G-Befehle und magere 4 M-Befehle verarbeiten.
Ich habe mir zwar einen Post-Prozessor fuer mein CAM-System geschrieben, um aus SurfCAM hgeraus G-Code fuer meinen Plotter auszugeben. Aber das ist halt nur fuer SurfCAM der Fall. Andere CAM-Systeme wissen nichts von meinem Plotter und geben halt ISO-Code aus. Dieser beinhaltet bspw. auch einen G2-Befehl, um Boegen zu fahren.
Das kann mein Plotter (noch) nicht.
Und somit versuche ich mit dem einlesen das Codes halt nicht unterstuetzte Befehle auszufiltern. Ebenso sollten die Kommentare ausgefiltert werden. (Start einen Kommentars: (, Ende: ))
Im Grunde sollte in der Funktion nur das ausgegeben werden, was auch vom Plotter unterstuetzt wird.

Ein % ist eher ein Steuerzeichen, das beim Ein/Auslesen (nicht der Abarbeiteung des NC-Programms) die Maschine weiss, hier ist das Programm zu Ende.

Mit einfache Sprache wollte ich nicht sagen, das sie trivial sei. Sie ist nur um Laengen simpler zu verstehen als ASM, Java oder Python.

Code: Alles auswählen

O0001
M6 T12
M3 S12000
G0 X30 Y30
G0 Z12 M8
(Das ist ein Kommentar)
G1 Z1 F200
G1 X30 Y60 F350
G1 X60 Y60
G1 X60 Y30
G1 X30 Y30
G1 Z20 F500
G0 Z200
M6 T0
M30
  1. Programmnummer 0001
  2. M6 = Wechsel ein Werkzeug ein - In diesem Falle T12
  3. M3 = Spindel drehen im Uhrzeigersinn / S12000 mit 12000 U/min-1
  4. G0 = im Eilgang (Schnellsmoegliche Bewegung der Achsen) auf Position X30/Y30
  5. das selbe nochmal auf Position Z12 (ohne X/Y-Bewegung)
  6. G1 = Fahre mit dem bei F (350) angegebenen Vorschub auf Z1 / ist dieser nicht angegeben, wird der zuletzt angegebene Wert genommen
  7. fahre auf Position X30/Y60 mit dem zuletzt angegebenen Vorschub (350)
In Zeile 11 Wird dann mit einem Vorschub von 500 Z bewegt und Z.13. legt das Werkzeug wieder ins Magazin ab, sodass sich in der Spindel keines mehr befindet. M30 sagt der Maschine, das das Programm nun zu Ende ist.

Ich koennte in den NC-Programmen Unterprogramme aufrufen, denen wenn noetig Uebergabeparameter mitgeben, in den NC-Programmen mathematische Funktionen nutzen ... von den Funktionen ist mein Plotter aber Meilenweit entfernt und ich habe auch gar keinen Anspruch darauf, das der das jemals kann. Deswegen eine Art Vorfilter, der alles nicht unterstuetztes ignoriert und nur die unterstuetzten Befehle an den Arduino sendet. (das geschieht dann via Serieller Schnittstelle)


@ Hyperion:
Was Du da gefunden hast, sind Programme, die G-Code aus 2D/3D-Daten erstellen.
Ich will ja schon erstellten G-Code auf dessen Kompatibilitaet mit meinem Plotter ueberpruefen. ;-)
Danke dennoch fuer den Einsatz :)
BlackJack

@hirnwunde: Also ich kann Texte über den ganzen Breitbildschirm auch nicht lesen, dass muss ich ja aber auch nicht, denn ich stelle ja nicht alles im Vollbild dar sondern jeweils so breit das ich es gut lesen kann. Und bei Fliesstext bricht das dann eben an den Stellen um bei denen *ich* die Breite durch das Fenster vorgebe. Und bei jemand anderem der es gerne breiter oder schmaler haben möchte kann der sich das auch so hinziehen wie er es gerne hätte. Diese Möglichkeit hat der Leser nicht mehr wenn der Autor selber Umbrüche dort einbaut wo *ihm* das Lesen am angenehmsten erscheint.

Also ob G-Code simpler ist als Assembler (die Sprache), also ohne irgendwelche Pseudo-Instructions für den Assembler (das Programm zum übersetzen), halte ich nicht zwingend für gegeben. Ein RISC-Prozessor oder so kleine 8-Bit-Prozessoren wie sie in den Heimrechnern früher verbaut wurden, sind ja auch nicht wirklich kompliziert. :-)

Das was Du bis jetzt beschreibst was mit dem Quelltext passieren soll kann eigentlich alles mit Textoperationen und ein paar Funktionen abgedeckt werden ohne dass man das bisschen Struktur in diesen Quelltexten in Datenstrukturen parst, mal von einer Liste mit einzelnen Zeilen abgesehen vielleicht.

Semantisch stelle ich mir das Löschen von unbekannten Befehlen ein wenig problematisch vor. Ist da nicht Gefahr gross, dass das Ergebnis unbrauchbar ist?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

hirnwunde hat geschrieben: @ Hyperion:
Was Du da gefunden hast, sind Programme, die G-Code aus 2D/3D-Daten erstellen.
Ich will ja schon erstellten G-Code auf dessen Kompatibilitaet mit meinem Plotter ueberpruefen. ;-)
Danke dennoch fuer den Einsatz :)
Ich habe sie mir ja auch nicht *genau* angeguckt, aber evtl. besitzen diese ja auch einen Parser, der einem das ganze schon in aufbereiteter Form präsentiert. Und auf dieser Struktur (im allgemeinen AST genannt) dürfte das Herausfiltern von Befehlen vermutlich einfacher sein, als selber einen Parser zu schreiben ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten