Parseur en python

[quote=“Histrion, post:20, topic: 55095”][/quote]

C’est une commande numérique de machine outil je crois, “CNC”

Une version qui corrige le saut de ligne mal parsé et qui enlève les doublons :
 

# -- coding:Latin-1 --
import sys

La machine a besoin de savoir quel genre d’action elle va entreprendre (le code G),

quel outil utiliser (le code T) et où effectuer l’action (coordonnées X, Y et Z).

* A chaque G rencontré, création d’une ligne dans le fichier output avec la valeur de G.

* Dans cette ligne du fichier output, écrire la première valeur de X, Y, Z qui suivent le G et la valeur de T qui précède le G dans le fichier input.

* Si dans la suite il y a un nouveau Y ou un nouveau X sans nouveau G, reproduire la ligne précédente en changeant la valeur de X ou de Y.

* Si G=172 suivi de G=186,création d’une double ligne avec G=172 et les premières valeurs de X,Y et Z rencontrées pour la première ligne. Pour la seconde ligne G=186 avec le même Y mais le plus grand X rencontré dans la suite avant un nouveau Y. Au nouveau Y, reproduire la double ligne précédente avec le nouveau Y.

*

stackG = []
stackT = []
stackX = []
stackY = []
stackZ = []

On ouvre le fichier à lire

f = open(“De atdedit.txt”, ‘r’)

Tant qu’il y a une ligne à lire :

for line in f:

Découper la ligne en une liste de champs

fields = line.split('\t')

Tant qu’il y a un champ à traiter dans la liste :

for field in fields:

On enlève les sauts de ligne parasites

	field = field.replace('\n', '')

Si le champ est un G :

	if field.startswith('G', 0, 1):

Ajouter la valeur de G à la liste de valeurs de G

		stackG.append(field)

Fin (si)

Si le champ est un T :

	if field.startswith('T', 0, 1):

Ajouter la valeur de T à la liste de valeurs de T

		stackT.append(field)

Fin (si)

Si le champ est un X :

	if field.startswith('X', 0, 1):

Ajouter la valeur de X à la liste de valeurs de X

		stackX.append(field)

Fin (si)

Si le champ est un Y :

	if field.startswith('Y', 0, 1):

Ajouter la valeur de Y à la liste de valeurs de Y

		stackY.append(field)

Fin (si)

Si le champ est un Z :

	if field.startswith('Z', 0, 1):

Ajouter la valeur de Z à la liste de valeurs de Z

		stackZ.append(field)

Fin (si)

############ LOGIQUE METIER

	if len(stackT) < 1 or len(stackX) < 1 or len(stackY) < 1 or len(stackZ) < 1:
		continue

	if len(stackG) > 1 and stackG[-1] == stackG[-2]:
		stackG.pop()

	while len(stackG) > 0:
		headG, *stackG = stackG
		print("{0}\t{1}\t{2}\t{3}\t{4}".format(headG, stackX[-1], stackY[-1], stackZ[-1], stackT[-1]))

############ FIN LOGIQUE METIER

Fin (tant que)

Fin (tant que)

On ferme le fichier qu’on lit

f.close()

En sortie j'ai ça :
 
G0      X-50    Y-9     Z17     T42/8
G40     X-50    Y-9     Z17     T42/8
G80     X-50    Y-9     Z17     T42/8
G90     X-50    Y-9     Z17     T42/8
G172    X-50    Y-9     Z17     T42/8
G101    X-50    Y-9     Z17     T42/8
G0      X-50    Y-9     Z17     T42/8
G1      X-50    Y-9     Z17     T42/8
G0      X-9     Y-50    Z19     T42/8
G100    X-9     Y-50    Z2      T4
G0      X-9     Y-50    Z2      T4
G81     X8      Y36     Z2      T4
G80     X8      Y342    Z8.5    T4
G0      X8      Y342    Z8.5    T4
G81     X660    Y36     Z8.5    T4
G80     X660    Y342    Z8.5    T4
G100    X660    Y342    Z8.5    T4
G0      X660    Y342    Z8.5    T4
G81     X8      Y36     Z8.5    T4
G80     X8      Y342    Z8.5    T4
G0      X8      Y342    Z8.5    T4
G81     X660    Y36     Z8.5    T4
G80     X660    Y342    Z8.5    T4
G172    X660    Y342    Z8.5    T5,6,7,8,9
G186    X660    Y342    Z8.5    T5,6,7,8,9
G0      X660    Y342    Z8.5    T5,6,7,8,9
G81     X94     Y36     Z8.5    T5,6,7,8,9
G80     X94     Y342.8  Z13     T5,6,7,8,9
G172    X94     Y342.8  Z2      T5,6,7,8,9
G186    X94     Y342.8  Z2      T5,6,7,8,9
G0      X94     Y342.8  Z2      T5,6,7,8,9
G81     X94     Y36     Z2      T5,6,7,8,9
G80     X94     Y342.8  Z13     T5,6,7,8,9
G172    X94     Y342.8  Z802    T43
G180    X94     Y342.8  Z802    T43
G0      X94     Y342.8  Z802    T43
G1      X6.4    Y382.4  Z6      T43
G0      X674.4  Y382.4  Z8      T43
[quote="laloutr3, post:21, topic: 55095"][/quote]
Du coup pour comprendre mieux le problème, ce serait bien de savoir pourquoi G172 et G186 sont spéciaux. Ca fait faire quoi à la machine ?

Edit : correction de la partie métier pour dépiler les G first-in first-out au lieu de last-in first-out.

Oui c’est bien une CNC.

G172 est spécial parce qu’il appelle une action ou l’outil va être actif entre le point X,Y,Z de départ et un point X,Y,Z d’arrivée. Le G172 donne donc le point de départ et appelle une autre action, par exemple G186 qui utilise des forets multiples qui ont un espacement défini (comme les séries de trous pour faire varier la hauteur des tablettes d’une étagère Ikea). Le G172 aura le X,Y, Z de départ et le G186 aura le X, Y, Z d’arrivée.

@Ben : oui je compte bien faire autant que possible pour travailler dans un bon ordre mais ça aide aussi beaucoup d’avoir le code d’Histrion pour voir à quoi doit ressembler la structure du programme.

@Histrion : j’imagine que tu vas dire que ça ne te prend pas beaucoup de temps pour sortir ce morceau de code mais quand même un grand merci, ça va beaucoup m’aider

[quote=« Miaouss, post:23, topic: 55095 »][/quote]

C’est pire que ça… Je suis dans une mission où je ne code plus beaucoup, alors c’est plutôt un moment de détente de faire un peu de python :slight_smile:

Le but est de codé des déplacement d’outils.
Les Gxxx sont des types ou des options de déplacement (tout droit, en arc de cercle, lentement, a fond, ect…)
X Y Z sont des positions (cible, vu que théoriquement on sais ou on est au départ du mouvement)
T c’est des outils (en général).

Jusque là c’est facile et Histrion a déjà bien prémaché le boulot :wink:

Après ou ça devient rigolos c’est que:
Les Gxxx sont groupé en « famille ». (Ex: Ceux qui définisse la vitesse VS ceux qui définisse le mouvement (tout droit, en arc de cercle gauche ou en arc de cercle droit) VS qu’elles outils sont interpolé ensemble).
Suivant les possibilité de ta CN t’aura plus ou moins de famille.
Certaines familles sont exclusive (on peut pas tourner à gauche en allant tout droit) et donc leur « G » s’annulent et d’autre pas (on peut synchronisé T1 et T2 pendant qu’on synchronise T3 et T4).

T’aura donc énormément de code métier qui va  beaucoup te compliquer la vie et pas forcément simplifier celle de tes potes. Le but ça va être de trouver le bon équilibre entre

A ta place je ferais ça:

  1. Demande leur de confirmer si ce que je dis est vrai (parce que c’est peut-être plus simple que ça sur votre CN)
  2. Demande leur de te définir les « familles » de Gxxx
  3. Code les familles principales et assez facile (G1, G100, G2, G3) tout en mettant dans le fichier final en commentaire ceux que tu interprete pas. (Comme ça ils peuvent faire les dernières retouche facilement à la main)
  4. Regarde avec eux la prochaine famille a ajouter et comment elle marche.
  5. Retourne au point 3 :wink:

    Bonne chance.

Je comprends un peu mieux le fonctionnel.

Une petite question à Miaouss. Ton but premier c’est de résoudre ce souci et aussi d’apprendre la programmation ou seulement de résoudre ce souci ?

Si le but c’est aussi d’apprendre la programmation il ne faudrait pas qu’on te mâche trop le travail, car tu n’apprendras rien :slight_smile: (AMHA)

[quote=“phili_b, post:26, topic: 55095”][/quote]
Clairement, le but premier c’est d’avoir une solution dans pas trop longtemps. Mais j’aimerais aussi être à même de comprendre ce qui est fait dans le programme si jamais il y a des adaptations à effectuer parce que je ne me vois pas revenir systématiquement demander qu’on règle les problèmes à ma place. Donc en fait c’est un peu les deux avec plus d’urgence sur la mise en place d’une solution fonctionnelle.

@Dadal: je vois que tu connais et que tu identifies bien le problème. Je sais bien que ça peut vite se compliquer. C’est pour ça que je n’ai proposé qu’une seule chose : chercher une solution pour ce qui constitue 90% de leur travail càd la fabrication de caissons. Les caissons, c’est simple, carré et ça utilise des ordres et des outils pas compliqués. Le reste on verra si j’arrive à m’en sortir assez avec python.

Bon alors hier j’ai continué mes cours et j’ai commencé à décortiquer le code d’Histrion. Pour le coup, les commentaires ont vachement bien aidé. Là je me concentre sur la partie “logique métier” pour voir comment faire pour que le fichier généré ne soit plus différent du fichier output que j’ai mis plus haut. Alors de ce que je comprends :

  1. Le premier if sert à passer les blocs qui ne présentent pas assez d'informations c'est ça ?
  2. Le second if je suis pas sûr. Tu fais une comparaison entre les valeurs en position -1 et en position -2 dans la liste de valeurs de G c'est ça ?
  3. Le while, c'est là où je comprends le moins. Surtout le "headG, *stackG = stackG"

Oui désolé les notations python sont “élégantes” (courtes et efficaces) mais pas forcément les plus claires au monde.

Point par point…


Au fur et à mesure qu’on lit le fichier source, on fait une pile de G, une pile de T, une pile de X, de Y et de Z (à chaque fois qu’on en rencontre un on le met au sommet de sa pile). Du coup le premier bloc if c’est pour dire que ça ne sert à rien d’écrire dans le fichier de sortie si on a pas encore au moins une valeur dans chaque pile, parce qu’on ne pourrait pas écrire une ligne au complet.

 

 
Un index négatif c'est en réalité pour dire "en partant de la fin" :
liste[-1] : le dernier élement de la liste
liste[-2] : l'avant dernier élement
etc.
liste[- len(liste)] : le premier élement
------------

Les append/pop c’est respectivement pour ajouter ou supprimer à la fin d’une liste.

Du coup, le code suivant sert à dire que si on vient de mettre deux fois la même valeur de G d’affilée dans la stackG, on n’en garde qu’une seule :

if len(stackG) > 1 and stackG[-1] == stackG[-2]:	
		stackG.pop()

------------

La syntaxe avec head ça sert à dire “à partir d’une liste tu me met d’un côté le premier élement (headG) et de l’autre le reste de la liste”. Du coup au lieu de retirer le dernier élement, on retire le premier. On veut ça et pas un pop()  parce que sinon quand on va “dépiler” les G on va les avoir dans l’ordre inverse par rapport au fichier source.

premier, *reste = liste

[quote=“Histrion, post:29, topic: 55095”][/quote]
Ok donc si je comprends bien, quand il rencontre un G, si des valeurs existent pour X,Y, Z et T, il écrit la ligne dans le fichier output. Du coup, dès la première valeur acquise pour T, X, Y, Z, une nouvelle ligne sera écrite dès qu’un G est rencontré c’est ça ?

Ca parait très con comme question mais si c’est bien comme ça que ça marche, il faudra en premier lieu que je cherche quel doit être le signal d’écriture d’une nouvelle ligne parce que souvent les valeurs pour X,Y,Z pour G suivent celui-ci.

C’ est tout à fait ça. Avec juste une subtilité qui est de rattraper les G qu’ on avait pas pu écrire parce qu’il nous manquait un T X Y ou Z.

Effectivement si l’ ordre d’ écriture n’est pas piloté comme ça il faudra probablement adapter le code.

Alors ces derniers jours on a travaillé du côté de mon pote pour avoir un fichier de départ le plus adapté possible (dans les limites de ce qu’on pouvait obtenir) afin de simplifier le code du parseur au maximum. [attachment=1972:De atdedit_sans rectif.txt]

Du point de vue des règles, voici déjà les premières qui semblent les plus générales (à partir du fichier de départ) : 

  • Si G >= 100, stacker la valeur dans la pile
  • Pour tout 172>=G>=100, stacker les valeurs de X, Y et Z qui viennent après le G0 qui suit (donc en gros, quand un G valable apparaît, attendre les valeurs X,Y,Z qui suivent le G0 pour écrire la ligne)
Est-ce qu'il y a moyen de lui dire d'écrire la ligne quand il rencontre un bloc "valeur Z" (après un G valable et un G0) et pas une nouvelle valeur Z (parce que les valeurs de Z peuvent être similaire alors que les actions sont différentes) ? 

Oui,
 
Au moment d’écrire le Z dans sa pile tu utilise un boléen qui permet de piloter ce que tu fais plus tard (il y a d’autres moyens, mais là ça sera plus simple pour découper).

-- coding:Latin-1 --

import sys

La machine a besoin de savoir quel genre d’action elle va entreprendre (le code G),

quel outil utiliser (le code T) et où effectuer l’action (coordonnées X, Y et Z).

* A chaque G rencontré, création d’une ligne dans le fichier output avec la valeur de G.

* Dans cette ligne du fichier output, écrire la première valeur de X, Y, Z qui suivent le G et la valeur de T qui précède le G dans le fichier input.

* Si dans la suite il y a un nouveau Y ou un nouveau X sans nouveau G, reproduire la ligne précédente en changeant la valeur de X ou de Y.

* Si G=172 suivi de G=186,création d’une double ligne avec G=172 et les premières valeurs de X,Y et Z rencontrées pour la première ligne. Pour la seconde ligne G=186 avec le même Y mais le plus grand X rencontré dans la suite avant un nouveau Y. Au nouveau Y, reproduire la double ligne précédente avec le nouveau Y.

*

stackG = []
stackT = []
stackX = []
stackY = []
stackZ = []

On ouvre le fichier à lire

f = open(“De atdedit.txt”, ‘r’)

Tant qu’il y a une ligne à lire :

for line in f:

Découper la ligne en une liste de champs

fields = line.split('\t')

Tant qu’il y a un champ à traiter dans la liste :

for field in fields:

Initialisation d’un booléen qui pilote l’écriture de la ligne

	writeLine = False

On enlève les sauts de ligne parasites

	field = field.replace('\n', '')

Si le champ est un G :

	if field.startswith('G', 0, 1):

Ajouter la valeur de G à la liste de valeurs de G

		stackG.append(field)

Fin (si)

Si le champ est un T :

	if field.startswith('T', 0, 1):

Ajouter la valeur de T à la liste de valeurs de T

		stackT.append(field)

Fin (si)

Si le champ est un X :

	if field.startswith('X', 0, 1):

Ajouter la valeur de X à la liste de valeurs de X

		stackX.append(field)

Fin (si)

Si le champ est un Y :

	if field.startswith('Y', 0, 1):

Ajouter la valeur de Y à la liste de valeurs de Y

		stackY.append(field)

Fin (si)

Si le champ est un Z :

	if field.startswith('Z', 0, 1):

Ajouter la valeur de Z à la liste de valeurs de Z

		stackZ.append(field)

Piloter l’écriture de la ligne

		writeLine = True

Fin (si)

############ LOGIQUE METIER

On écrit la ligne que dans les cas prévus

	if writeLine:
		# ...

############ FIN LOGIQUE METIER

Fin (tant que)

Fin (tant que)

On ferme le fichier qu’on lit

f.close()

Ok alors j’ai pris le temps pour essayer de bien piger ce que le code faisait déjà et comment je devrais le modifier. Donc si je comprends bien (hourra pour les commentaire :wink: ), writeline devient true quand Z trouve une valeur ce qui me permettra de conditionner l’écriture de la ligne. Du coup je devrai préciser dans la logique métier que : 

# Vu la structure du fichier, chaque fois que writeline devient True et le G à écrire dans la ligne 
# est en position -3 dans la pile, sauf si ce G>172. Les valeurs de X, Y et Z sont par contre à -1. 
# Ca donnerait quelque chose du genre
If writeLine == True and stackG[-3] < 172
#  On écrit la ligne avec les valeurs [G-3],[X-1],[Y-1],[Z-1],[T-1] 
print...
A ce stade j'ai déjà un double soucis. 
  • Premièrement, quand G<172 il faut que je puisse dire que quand des champs rencontrés directement après Z sont des valeurs de X ou Y, il faut réécrire la ligne précédente à l'identique sauf pour la valeur de X ou Y qui doit être la nouvelle valeur rencontrée.
  • Deuxièmement, quand G>=172 et writeLine=True il faut que j'écrive une double ligne où la première renvoie les valeurs [G-4],[X-1],[Y-1],[Z-1],[T-1]. Par contre, la deuxième ligne à écrire doit reprendre [G-3] et la plus grande valeur de X rencontrée dans la série de valeurs de X qui suivent directement. Cette valeur de X est repérée par le fait qu'elle précède une valeur de Y.
Je sais pas si je suis très clair ? 

J’ai bien peur qu’en partant de manière aussi empirique/naïve (ce n’est pas péjoratif) tu n’atteignes pas le but que tu veux et que le post de Dadal plus tôt dans le thread est la bonne piste. Il faut que tu listes ce que sont vraiment ces Gxxx, en français, et lesquels sont compatibles entre eux. Et même comme ça je pense que l’algorithme risque d’être un peu coton à écrire (surtout si la notion de tâches parallélisables rentre en ligne de compte).

Juste pour info, le logiciel de l’outil il est open source ? Ou même s’il est propriétaire tu as accès au code source de l’ancienne version et/ou de la nouvelle ?

[quote=“Histrion, post:35, topic: 55095”][/quote]
Non évidemment le logiciel n’est pas open source malheureusement.

Mais qu’est-ce qui te fait dire que je n’y arriverai pas comme ça ? C’est mon approche qui est mauvaise ? Ou bien le fait que je sois débutant par rapport à la complexité de ce qu’il y a à faire ? Quelque part, si le code est un peu usine à gaz ça ne pose pas trop de problèmes du moment que ça marche…

En fait ce que j’appelle une approche naïve c’est de partir de rien et d’affiner en ajoutant des règles, une à une, pour s’approcher du résultat souhaité. Ca peut marcher si l’algorithme de base est simple. Mais s’il est compliqué, tu n’y arriveras pas (et ça n’a rien à voir avec le fait d’être débutant, même quelqu’un d’expérimenté pourrait y passer beaucoup de temps).

Et là l’algorithme a l’air d’être entre les deux… Ni trop simple pour qu’on puisse t’aider directement, ni suffisamment compliqué pour que ça saute aux yeux que ça va être très chiant à faire. Tu es dans le pire cas pour décider si l’approche est la bonne ou s’il vaut mieux abandonner… :<br>
Mais vu le post de Dadal, j’ai l’impression qu’il y a une notion de “tâches en parallèle” qui rentre en ligne de compte (en gros on peut faire Gxxx et Gyyy en même temps du point de vue de la machine, et donc peut-être aussi du point de vue de l’algorithme qui sert à écrire l’ancien format de fichier).

A titre personnel ça m’éclairerait beaucoup de savoir ce que sont les G. Ca fait toute la différence dans le ressenti que j’ai de ton problème : sans savoir ce que les G sont, j’ai l’impression que c’est compliqué.

Sinon, vous voulez pas faire comme j’ai expliqué au debut : commencer par ecrire ce que doit faire le programme clairement avant de coder ?

Vous verrez ensuite quel type d’algo implémenter une fois que vous aurez listé toutes les regles.

[quote=« Miaouss, post:36, topic: 55095 »][/quote]
Les 2 mon général. :slight_smile:

Ton approche est de toute façon mauvaise puisqu’il faut écrire en français ce que doit faire le programme, comme le dit Ben, puisque ton problème n’est pas si simple. Mais comme tu es débutant tu as encore moins de chance d’y arriver que quelqu’un d’expérimenté qui pourra éventuellement le faire sur un coin de table grâce à son expérience justement.

Mais l’informatique, et l’algo en particulier, sont passionnant car à partir d’une analyse fonctionnelle faite par quelqu’un d’autre, on peut quand même lui programmer son truc. Tu penses pouvoir t’en passer car tu es l’analyste fonctionnel et le programmeur, mais c’est faux, dès que c’est un peu complexe on est obligé de faire de l’analyse fonctionnelle, même pour soi même :slight_smile:

@phili_b et Ben,

Pour modérer vos réponses l’approche qui consiste à partir de la fin (rétro-ingénierie) est bien adaptée au cas présent : un format de fichier “obscur”, dont l’algorithme qui a servi à l’écrire n’est pas accessible… On est en plein dans le bon cas où “je teste, je vois, j’adapte” peut fonctionner. Sauf qu’ici ça prendra peut-être plus de temps que d’essayer de re-rédiger les spécs fonctionnelles. Mais rien n’est moins sûr. C’est la zone grise :\