Návrh a realizácia vývojového prostredia pre jednočipové PLC

Z Kiwiki
Skočit na navigaci Skočit na vyhledávání
Tnu wiki.png
Trenčianska Univerzita Alexandra Dubčeka v Trenčíne
Fakulta Mechatroniky
Fm wiki.png
Návrh a realizácia vývojového prostredia pre jednočipové PLC

zadanie práce
Diplomová práca


Autor:
Pedagogický vedúci:

Ing. Viliam Šimovič, PhD.

Študijný odbor: Mechatronika

Akademický rok

2009/2010

Abstrakt

Cieľom tejto diplomovej práce je vytvoriť softvérové vybavenie na prácu s jednočipovým PLC. Aplikácia bola vytvorená v jazyku Python s použitím API knižnice wxWidgets a pozostáva s niekoľkých modulov. Najdôležitejšou časťou aplikácie je prekladač jazyka STL. Výsledkom prekladu je strojový kód mikrokontroléra Atmel AVR.

Ďalej aplikácia dokáže zabezpečovať komunikáciu s PLC cez sériový alebo USB port, konfigurovať a používať ovládače IIC zariadení a terminálového modulu.

Súčasťou je aj editor a simulátor v jazyku LAD, nastroj na online sledovanie stavu pamäti.

Abstract

The intention of this thesis to realize software tool for work with single-chip PLC. Application was developed in Python programing language. It used API library wxWidgets and it consist of several modules. The most important part of application is STL compiler. The result of STL program compilation is Atmel AVR processor code.

Next, application can control serial or USB communication with PLC,it can configure or use IIC and terminal drivers.

Part of application is LAD editor, simulator and online memory watching tool.

Úvod

Cieľom práce Návrh a realizácia vývojového prostredia pre jednočipové PLC je vytvoriť užívateľský program na obsluhu, programovanie a debuggovanie jednočipových PLC. Táto práca úzko súvisí s prácou Bc. Mariána Sovu s názvom „Návrh a realizácia jednočipového PLC“, keďže sa tieto práce vzájomne dopĺňajú. Navrhovaný program by mal spĺňať niekoľko základných parametrov. Multiplatformovosť, intuitívne ovládanie, možnosť programovania v jazyku ST, LAD. Ďalej by mal obsahovať nástroj na odhaľovanie chýb, editor ovládačov externých IIC zariadení, nástroj na kontrolu stavu pamäte simuláciu a iné pokročilé možnosti pri projektovaní zariadenia s PLC.

V prvej kapitole sa zaoberám kompilátorom jazyka ST. Rozoberám štruktúru súborov lexikálnej a syntaktickej analýzy a tiež konfiguráciu rôznych typov PLC.

V druhej kapitole rozoberám realizáciu kompilátora IIC datablokov a spôsoby použitia v projekte.

V tretej kapitole sa zaoberám komunikáciou s PLC. Formátom odosielaných dát a tiež základnými pravidlami riadenia komunikácie medzi PLC a počítačom.

V poslednej, štvrtej kapitole opisujem užívateľské prostredie a konkrétne možnosti využitia modulov z predchádzajúcich kapitol.


Kompilátor jazyka ST

Účelom tejto kapitoly je vytvoriť prekladač zdrojového kódu jazyka ST. Prekladač alebo tiež kompilátor, je možné definovať prostredníctvom konečného stavového automatu. Pre jazyk Python je k dispozícii modul ply.py. Je to čisto Pytonovská implementácia nástrojov na vytváranie komilátora, LEX a YACC. Tieto moduly slúžia na vytvorenie takéhoto konečného stavového automatu. Súčasne musí výsledný kompilátor umožňovať užívateľskú konfiguráciu. Pre tento účel bol zvolený jednoduchý model konfiguračných súborov XML.


Obrázok 1: Kompilačná sekvencia

Lexikálna analýza zdrojového kódu

Modul lex.py slúži na spracovanie vstupného textu do kolekcie tokenov, vytvorenej prostredníctvom množiny regulárnych výrazov. Zjednodušene, lexikálna analýza tokenizuje vstupný reťazec. Informácie v tejto časti sú čerpané z [9],[10].

Vstupný reťazec 
    x = 3 + 42 * (s – t)

Tokenizér rozdelí na individuálne tokeny

    'x','=', '3', '+', '42', '*', '(', 's', '-', 't', ')'

Tokenom sú potom pridelené mená kvôli identifikácii

'ID','EQUALS','NUMBER','PLUS','NUMBER','TIMES',
'LPAREN','ID','MINUS','ID','RPAREN'

Nakoniec je vstup rozdelený do párov pozostávajúcich s typu tokenu a hodnoty.

('ID','x'), ('EQUALS','='), ('NUMBER','3'), 
('PLUS','+'), ('NUMBER','42'), ('TIMES','*'),
('LPAREN','('), ('ID','s'), ('MINUS','-'),
('ID','t'), ('RPAREN',')'

Štruktúra zdrojového kódu pre LEX

Implementované pravidlá lexikálnej analýzy tvoria samostatný modul ktorý používa knižnica LEX. Výstup tohto modulu, sled tokenov, využíva modul YACC pre syntaktickú analýzu. Syntax zápisu modulu pozostáva z niekoľkých častí.


import ply.lex as lex
# Zoznam mien tokenov
tokens = ('ID','ID2',...)
# Regulárne výrazy pre jednoduché tokeny
# 't_'+Meno_tokenu
t_ID = r'regulárny výraz' 
# Regulárny výraz so zdrojovým kódom 
def t_ID2(t):
        r'regulárny_výraz'
        t.value=10
        …
        return t
# Definovanie pravidla pre sledovanie počtu riadkov
def t_newline(t):
        r'\n+'
        t.lexer.lineno+=len(t.value)
# Ignorované reťazce
t_ignore=r' \t'
# Zachytávanie chýb
def t_error(t):
        print "Illegal character '%s'" % t.value[0]
        t.lexer.skip(1)
# Vytvoríme samotný lexer
lex.lex()


Ukážka 1: Štruktúra zdrojového kódu pre LEX


Prvou povinnou časťou je importovanie modulu LEX a definovanie listu tokenov. Tento list je takisto povinný a obsahuje všetky možne mená tokenov, ktoré môžu byť vytvorené lexerom. Tento list taktiež využíva modul yacc.py na identifikovanie výrazov.

Každý token je špecifikovaný regulárnym výrazom. Každé toto pravidlo je definované pomocou premennej s predponou t_ ktorá identifikuje premennú ako token. Pre jednoduché výrazy, definujeme regulárny výraz ako reťazec ktorý je obsahom premennej. Pre zložitejšie výrazy je token definovaný ako funkcia. Regulárny výraz je zadaný ako dokumentačný reťazec za hlavičkou funkcie. Funkcia vždy preberá jeden argument ktorý je inštancia LexTokenu. Tento objekt ma viacero atribútov, t.type typ tokenu (reťazec), t.value je lexovaný výraz, t.lineno je aktuálne číslo riadku, t.lexpos pozícia tokenu relatívne od začiatku vstupného textu, t.type meno tokenu.

Pretože lex.py nevie nič o počtu riadkov, je potrebné aktualizovať tieto informácie pomocou špeciálneho pravidla.

Špeciálne pravidlo t_ignore je rezervované modulom lex.py pre ignorované znaky. Najčastejšie sa používa pre „biele miesta“ vo vstupnom prúde.

Nakoniec t_error je funkcia na zachytávanie chýb v lexikálnej analýze. Táto funkcia je volaná zakaždým keď je detekovaný neprípustný reťazec.

Lexikálna analýza jazyka STL

Zoznam tokenov, alebo tiež zoznam príkazov jazyka STL, je rozdelený do štyroch skupín, podľa počtu parametrov s ktorými asociovaný príkaz pracuje. Toto delenie je dôležite pre následnú syntaktickú analýzu, kde slúži na odhaľovanie chýb. V spracovávanom texte sú vyhľadávané znaky a symboly definovane ako t_ pravidla. Ak pre daný reťazec nieje definovaný token, je vykonaná záverečná kontrola či sa nenachádza v niektorom zo štyroch zoznamov príkazov alebo nejde o definíciu adresy pamäti. V prípade úspechu je návratovou hodnotou príslušný token, v opačnom prípade navracia token error. Pre jazyk STL sme definovali tieto základné tipy tokenov:


 t_ignore = ' \t\r'
Ukážka 2: Definícia tokenu (t_ignore)

ignore – pomocný token odstraňujúci zo spracovávaného textu znaky bez informačnej hodnoty. Tento token sa ďalej v syntaktickej analýze nepoužíva.

 t_NUMBER =      r'\d+'
Ukážka 3: Definícia tokenu (t_NUMBER)

NUMBER – token definujúci celočíselnú kladnú konštantu.

PLUS – token znamienka (+).

 t_PLUS =      r'\+'
Ukážka 4: Definícia tokenu (t_PLUS)

MINUS – token znamienka (-).

 t_MINUS =      r'-'
Ukážka 5: Definícia tokenu (t_MINUS)

DOT – token znamienka (.) .

 t_DOT =      r'\.'
Ukážka 6: Definícia tokenu (t_DOT)

COMMA – token znamienka (,). Veľmi často sa používa v syntaktickej analýze na identifikovanie príkazov, kde oddeľuje jednotlive parametre.

 def t_COMMA(t):
r'\,'
global mem
mem = False
return t
Ukážka 7: Definícia tokenu (t_COMMA)

BIT – token reprezentujúci bitovú časť adresy. Hodnota tokenu je číslo predstavujúce adresu bitu.

 def t_BIT(t):
r'\.[0-7]'
global mem
mem = False
t.value = t.value[1]
return t
Ukážka 8: Definícia tokenu (t_BIT)

FLOAT – token reprezentujúci číselnú premennú typu float. Zápis tejto premennej je možný v rôznych tvaroch. Napr. (3.14 1e100 3.14e-10 0e0 -1.14e-3). Tento token sa ďalej v syntaktickej analýze nevyskytuje. Ak sa jedná o číselnú konštantu a nie adresu v pamäti, je hodnota float uložená ako token NUMBER a jeho hodnota je skutočný tvar premennej float. (tzn. bajtová reprezentácia čísla v pamäti)

 def t_FLOAT(t):
r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?'    
       if mem: 
            global mem
            mem = False
            t.type = 'FULLBIT'           
            return 
      global mem
      mem = False 
      t.value = exp(expf(float(t.value)))
      t.type = 'NUMBER'
      return t
     
Ukážka 9: Definícia tokenu (t_FLOAT)

ID – token symbolizujúci reťazec ktorý nebol doposiaľ identifikovaný ako token. Jedná sa o pomocný token, ktorý vyhodnocuje či ide o token COMM (command „príkaz“), alebo MEMO (memory „pamäť“).

 def t_ID(t):
r'[a-zA-Z=<>][a-zA-Z=<>]*'
if t.value.upper() in keywords:
global mem
mem = False
t.type = 'COMM'
t.value = t.value.upper()
elif t.value.upper() in memory:
t.type = 'MEMO'
global mem
mem = True
t.value = t.value.upper()
elif t.value == '=':
print mem
mem = False
t.type = 'COMM'
return t
Ukážka 10: Definícia tokenu (t_ID)

COMM – token príkazu ktorého meno je uložené v zozname keywords. Hodnota tokenu predstavuje jeho meno.

MEMO – token špecifikujúci oblasť pamäti. Je súčasťou plnej adresy PLC. Jeho možné tvary sú definované v zozname memory. COMMENT – token poznámky alebo komentára. Slúži len pre potrebu programátora pracujúceho v STL kóde alebo pomôcka na identifikáciu vo vývojovom prostredí.

 def t_COMMENT(t):
r'\#.*'
global mem
mem = False
Ukážka 11: Definícia tokenu (t_COMMENT)

NEWLINE – token nového riadku. Používa sa v dvoch prípadoch. Ako identifikátor nového riadku pre počítadlo a tiež ako ukončenie výrazu v syntaktickej analýze.

 def t_NEWLINE(t):
r'\n|\r'
global mem
mem = False
if first:
t.lexer.lineno = 0
global first
first=0
t.lexer.lineno += len(t.value)
global size
size+=1
return t
Ukážka 12: Definícia tokenu (t_NEWLINE)

ERROR – token chybového stavu. Je ním identifikovaný každý výraz ktorý nemá definovaný token. Ak je token ERROR identifikovaný, lexikálna analýza sa ukončí a program vypíše chybové hlásenie.

 def t_error(t):
print "Illegal character '%s' " % (t.value[0])
t.lexer.skip(1)
Ukážka 13: Definícia tokenu (t_ERROR)

Syntaktická analýza

Nástrojom na syntaktickú analýzu je modul yacc.py. Tento modul umožňuje v spolupráci s modulom lex.py definovať pravidlá, ako má budúci jazyk vyzerať. Tzn. umožňuje vytvárať programátorské konštrukcie a zápisy. YACC využíva parsovacie techniky známe ako LR-parsing alebo shift-reduce parsing. Informácie v tejto časti sú čerpané z [9],[10].

Štruktúra zdrojového kódu pre YACC

Zdrojový kód pre YACC tvorí samostatný modul pozostávajúci z niekoľkých častí.


      import ply.yacc as yacc
# List tokenov
from userlex import tokens
# Gramatické pravidlá
def p_expression(p):
    'expression : expression PLUS term'
    p[0] = p[1] + p[3]
def p_expression_term(p):
    'expression : term'
    p[0] = p[1]
def p_term_factor(p):
    'term : factor'
    p[0] = p[1]
def p_factor_id(p):
    'factor : ID'
    p[0] = p[1]
# Pravidlo pre chyby
def p_error(p):
    print "Syntax error in input!"
yacc.yacc()


Ukážka 14: Štruktúra zdrojového kódu pre YACC


Prvou povinnou časťou je importovanie modulu yacc.py a listu tokenov zo zdrojového súboru, modulu lexikálnej analýzy.

Každé gramatické pravidlo je definované ako Pythonovská funkcia, kde dokumentačný reťazec obsahuje presnú gramatickú štruktúru daného pravidla. Každá funkcia preberá argument p ktorý je sekvencia obsahujúca hodnotu každého gramatického symbolu podľa odpovedajúceho pravidla.



  def p_expression_plus(p):
    'expression : expression PLUS term'
    #   ^            ^        ^    ^
    #  p[0]         p[1]     p[2] p[3]
    p[0] = p[1] + p[3]


Ukážka 15: Definovanie gramatického pravidla


Pravidlo p_error(p) je definované pre zachytávanie syntaktických chýb. Keď sa vyskytne syntaktická chyba počas analýzy, je ihneď zachytená. Túto chybu je nutné ďalej spracovať kvôli debugging-u.

Na vytvorenie inštancie parseru, je nutné volať funkciu yacc.yacc(). Táto funkcia vytvorí LR parsovacie tabuľky podľa preddefinovanej gramatiky.


Syntaktická analýza jazyka STL

Syntax jazyka STL je definovaná veľmi jednoducho. Celý program pozostáva s príkazov a parametrov príkazu. Syntaktické pravidlo pre program je teda definované, ako kolekcia príkazov a ich parametrov. Výsledkom je list pevne usporiadaných príkazov a parametrov vhodných na spracovanie. Syntaktické pravidlo príkazu je definované odlišne. Počet parametrov ako aj ich typ, je závislý od príkazu. V tomto kroku používame štyri kategórie príkazov zadefinovaných v module lexikálnej analýzy. Podľa zoznamu v ktorom sa príkaz nachádza, sa k príkazu pripoja príslušné parametre. Parameter príkazu definuje číslo alebo adresa. Syntax adresy je dvojakého druhu. Adresa bitová a bajtová.

Modul syntaktickej analýzy STLParser.py obsahuje niekoľko gramatických pravidiel ktoré popisujú jazyk STL:

Adresy – Na opísanie adresy v pamäti PLC je definovaných niekoľko pravidiel, aby parser dokázal identifikovať všetky možné kombinácie a tvary adresného reťazca a tiež slúžia na odhaľovanie chybného tvaru adresy. *

 def p_adress_bite(p):
adress : MEMO NUMBER BIT
p[0] = (p[1],int(p[2]),int(p[3]))
Ukážka 16: Definícia tokenu (adress : MEMO NUMBER BIT)

Adresa definovaná identifikátorom pamäti, bajtom a bitim

 def p_adress_fullbite(p):
adress : MEMO FULLBIT
i = p[2].find('.')
p[0] = (p[1],int(p[2][:i]),int(p[2][i+1:]))
Ukážka 17: Definícia tokenu (adress : MEMO FULLBIT)

Adresa definovaná identifikátorom pamäti a desatinným číslom

 def p_adress_byte(p):
adressb : MEMO NUMBER
#print p
p[0] = (p[1],int(p[2]),0)
Ukážka 18: Definícia tokenu (adressb : MEMO NUMBER)

Adresa definovaná ako identifikátor pamäti a číslo – bajtová adresa

 def p_adress_byte_bad(p):
adressb : MEMO error
#print p
p[0]= '<ERROR> WRONG BYTE ADRESS IN LINE %d ' % (p[2].lineno-_LINES)
Ukážka 19: Definícia tokenu (adressb : MEMO error)

Chybne definovaná adresa s chybou v bajtovej časti adresy. Návratová hodnota je chybový reťazec s popisom chyby a jej umiestnením.

Chybne definovaná adresa s chybou v bitovej časti adresy. Návratová hodnota je chybový reťazec s popisom chyby a jej umiestnením.

 def p_adress_bite_bad(p):
    adress : MEMO NUMBER error
    p[0]= '<ERROR> WRONG BITE OR BYTE ADRESS IN LINE %d ' % (p[3].lineno-_LINES)
Ukážka 20: Definícia tokenu (adress : MEMO NUMBER error)

Príkazy – Príkazy STL sú rozdelené do niekoľkých skupín podľa parametrov ktoré daný príkaz vyžaduje, preto aj gramatické pravidlá delíme na tieto skupiny. V každej definícii je urobená kontrola do ktorej skupiny identifikovaný príkaz patrí a skontroluje sa počet parametrov.


 def p_command(p):
command : COMM
if p[1] in ONE:
p[0]=(p[1],None)
else: p[0] = '<ERROR> WRONG ARGUMENTS COUNT ET " %s " IN LINE %d'% (p[1],p.lineno(1)-_LINES)
Ukážka 21: Definícia tokenu (command : COMM)

Bezparametrový príkaz. Jediný token ktorý tvorý príkaz je samotný token COMM.

 def p_command_byte(p):
command : COMM adressb
if isinstance(p[2],str):
p[0]=p[2]
elif p[1] in TWO:
p[0] = (p[1],p[2])
else: p[0] = '<ERROR> WRONG ARGUMENTS COUNT ET " %s " IN LINE %d'% (p[1],p.lineno(1)-_LINES)
Ukážka 22: Definícia tokenu (command : COMM adressb)

Jednoparametrový príkaz. Token je tvorený s kombinácie príkazu a adresy bajtu alebo bitu.

 def p_command_bite(p):
command : COMM adress
if isinstance(p[2],str):
p[0]=p[2]
elif p[1] in TWO :
p[0] = (p[1],p[2])
else: p[0] = '<ERROR> WRONG ARGUMENTS COUNT ET " %s " IN LINE %d'% (p[1],p.lineno(1)-_LINES)
Ukážka 23: Definícia tokenu (command : COMM adress)

Chybný príkaz s jedným parametrom.

 def p_command_bite_bad(p):
command : COMM error
#print p[0],p[1],p[2]
p[0] = '<ERROR> BAD ADRESS SYNTAX ET " %s " IN LINE %d ' % (p[1],p[2].lineno-_LINES)
Ukážka 24: Definícia tokenu (command : COMM error )

Dvojparametrový príkaz. Token tvorí identifikátor príkazu, adresa, čiarka a posledný parameter môže byť adresa alebo konštanta.

 def p_command_byte_value_bad(p):
command : COMM adressb COMMA error
p[0] = '<ERROR> BAD SYNTAX ET " %s " IN LINE %d ' % (p[1],p[4].lineno-_LINES)
Ukážka 27: Definícia tokenu ( command : COMM adressb COMMA error)
 def p_command_byte_value(p):
command : COMM adressb COMMA NUMBER
#print p[1],THREE
if isinstance(p[2],str):
p[0]=p[2]
elif p[1] in THREE:
p[0] = (p[1],p[2],int(p[4]))
else: p[0] = '<ERROR> WRONG ARGUMENT COUNT IN LINE '+str(p.lineno(1))
Ukážka 26: Definícia tokenu ( command : COMM adressb COMMA NUMBER)
 def p_command_byte_byte(p):
command : COMM adressb COMMA adressb
#print p[1],THREE
if isinstance(p[2],str):
p[0]=p[2]
elif isinstance(p[4],str):
p[0]=p[4]
elif p[1] in THREE:
p[0] = (p[1],p[2],p[4])
else: p[0] = '<ERROR> WRONG ARGUMENT COUNT IN LINE '+str(p.lineno(1))
Ukážka 25: Definícia tokenu ( command : COMM adressb COMMA adressb)

Chybný príkaz s dvoma parametrami. Ak nastane problém v prvom parametri zachytí sa táto chyba ako u jednoparametrového príkazu, preto stačí definovať chybný token pre druhý parameter.

 def p_command_byte_value_bad_con(p):
command : COMM adressb error
p[0] = '<ERROR> BAD SYNTAX ET " %s " IN LINE %d ' % (p[1],p[3].lineno-_LINES)
Ukážka 28: Definícia tokenu ( command : COMM adressb error)

Trojparametrový príkaz. Tvorí ho identifikátor príkazu, čiarka, adresa, čiarka, adresa a posledný parameter je konštanta alebo adresa.

 def p_command_byte_byte_number(p):
command : COMM adressb COMMA adressb COMMA NUMBER
#print p[1],THREE
if isinstance(p[2],str):
p[0]=p[2]
elif isinstance(p[4],str):
p[0]=p[4]
elif p[1] in FOUR:
p[0] = (p[1],p[2],p[4],int(p[6]))
else: p[0] = '<ERROR> WRONG ARGUMENT COUNT IN LINE '+str(p.lineno(1))
Ukážka 29: Definícia tokenu ( command : COMM adressb COMMA adressb COMMA NUMBER)
 def p_command_byte_byte_byte(p):
command : COMM adressb COMMA adressb COMMA adressb
#print p[1],THREE
if isinstance(p[2],str):
p[0]=p[2]
elif isinstance(p[4],str):
p[0]=p[4]
elif isinstance(p[6],str):
p[0]=p[6]
elif p[1] in FOUR:
p[0] = (p[1],p[2],p[4],p[6])
else: p[0] = '<ERROR> WRONG ARGUMENT COUNT IN LINE '+str(p.lineno(1))
Ukážka 30: Definícia tokenu ( command : COMM adressb COMMA adressb COMMA adressb)

Chybný príkaz s tromi parametrami, s chybou v poslednom parametre. Rovnako ako u dvojparametrového definovania chyby aj v tomto prípade zachytí chybu v prvom parametre jednoparametrová chyba a v druhom dvojparametrová chyba.

 def p_command_byte_byte_value_bad(p):
    command : COMM adressb COMMA adressb COMMA error
    p[0] = '<ERROR> BAD SYNTAX ET " %s " IN LINE %d ' % (p[1],p[6].lineno-_LINES)
Ukážka 31: Definícia tokenu ( command : COMM adressb COMMA adressb COMMA error)
 def p_command_byte_byte_value_bad(p):
    command : COMM adressb COMMA adressb COMMA error
    p[0] = '<ERROR> BAD SYNTAX ET " %s " IN LINE %d ' % (p[1],p[6].lineno-_LINES)
Ukážka 32: Definícia tokenu ( command : COMM adressb COMMA adressb COMMA error)

Výrazy – Pod pojmom výraz v STL parsere, rozumieme akýkoľvek príkaz alebo token ukončený enterom.

 def p_statement(p):
    statement : command NEWLINE
                 | command
    if isinstance(p[1],str):
        print p[1]
        p[0] = None
        p.parser.error = 1
    else:
        p[0] = (p[1])
Ukážka 33: Definícia tokenu ( statement : command NEWLINE | command)
 def p_statement_bad(p):
    statement : error NEWLINE
    print "<ERROR> MALFORMED STATEMENT AT LINE:", p[1].lineno-_LINES
    p[0] = None
    p.parser.error = 1
Ukážka 34: Definícia tokenu ( statement : error NEWLINE)
 def p_statement_newline(p):
    statement : NEWLINE
    p[0] = None
Ukážka 35: Definícia tokenu ( statement : NEWLINE)

Program – Program predstavuje kolekciu po sebe idúcich výrazov s ktorých žiaden nieje token error. Ide od výsledok parsovacieho procesu a vstup do záverečného modulu kompilátora.


 def p_program(p):
    program : program statement
               | statement
    p.parser.lineno=0
    if len(p) == 2 and p[1]:
       p[0] = []
       p[0].append(p[1])
    elif len(p) ==3:
        p[0]=p[1]
        if not p[0]: p[0]=[]
        if p[2]:
            p[0].append(p[2])


Ukážka 36: Definícia tokenu ( program : program statement | statement)
 def p_program_error(p):
    program : error
    p[0] = None
    p.parser.error = 1
Ukážka 37: Definícia tokenu ( program : error)

Definovanie konfiguračného súboru PLC

Kvôli miernej odlišnosti architektúry jednotlivých typov mikrokontrolérov AVR, je nutné definovať pre každý typ mikrokontroléra konfiguračný súbor. Tento súbor obsahuje špecifické informácie o pamäťových oblastiach, podporovaných príkazoch STL a pod..


Štruktúra konfiguračného súboru PLC

Konfiguračný súbor PLC je XML súbor s pevne definovanou stromovou štruktúrou. Jednoduchosť textového formátu a prehľadnosť zápisu, umožňuje rýchlo a flexibilne nakonfigurovať nový typ PLC, bez nutnosti prepisovania zdrojových kódov kompilátora.


 <?xml version="1.0" encoding="UTF-8"?>
<MCU type="myPLC32" description="Zakladna verzia plc zalozena na ATMega32">
 <Adress>
  <P   count="4"   memory="0000" size="1" />
  <PA  count="16"  memory="0005" size="16" />
  <SP  count="10"  memory="0015" size="1" />
  <T   count="384" memory="0029" size="16" />
  <C   count="384" memory="01A9" size="16" />
  <V   count="377" memory="0329" size="1" />
 </Adress>
 <Command list="LD LDN A AN O ON ALD OLD LPR LPS LPP NOT S R PE NE TON TOFF = MOVB ADDB SUBB ==B <>B <B >B <=B >=B INCB DECB"/>
 <Config>
    <Datablock size="128"/>
    <Memory offset="006E"/>
    <Port list='FF FF BF B0 00 00 00'/>
 </Config>
</MCU>


Ukážka 38: Štruktúra konfiguračného súboru PLC

Prvou časťou súboru je hlavička xml s definovanou verziou xml a kódovaním znakovej sady. Nasleduje koreňová sekcia MCU s parametrami type definuje typ PLC a description so základným popisom PLC. Táto sekcia obsahuje vetvu Address ktorej podvetvy definuje adresný priestor PLC a vetvu Command s parametrom list, obsahujúcim príkazy jazyka ST ktoré PLC podporuje. Adresa je definovaná menom podvetvy, počtom bytov count, začiatočnou 16bitovou adresou v pamäti mikrokontroléru memory a veľkosťou jednej bunky definovaného adresného priestoru v bitoch size. Je dôležité spomenúť, že kompilátor umožňuje prekrývanie pamäťového priestoru, tzn. jedinečne adresované miesto mikrokontroléra môže byť adresované pomocou viacerých adries PLC. Vetva Config definuje špecifické parametre PLC, ako veľkosť dátového bloku odosielaného programu, offset pamäte a porty ktoré obsahuje PLC.


Spolupráca programov LEX a YACC

V tejto časti popisujem spoluprácu programových modulov lex.py a yacc.py pri vytvorení prekladača jazyka ST. Výsledný modul STL.py bude kompilovať zdrojový kód v jazyku ST na strojový kód mikrokontroléra Atmel AVR. Princíp činnosti je vysvetlený na obrázku 2.

Princíp spolupráce je možne opísať v niekoľkých bodoch:

  1. Nakonfigurovaný lexikálny analyzátor STLex.py vyhľadá v zdrojovom kóde tzv. Tokeny (definované kľúčové slová).
  2. Zadefinujeme syntaktický analyzátor STLparser.py (vyhľadá možné kombinácie tokenov a spracováva chybové stavy).
  3. Definujeme konfiguračný súbor PLC pre ktoré je program kompilovaný.
  4. Kompilačnému modulu STL.py načítame konfiguračný súbor a zdrojový kód jazyka ST.
  5. Skompilovaný program je sekvencia strojových kódov, určených pre zadefinované PLC.
Obrázok 2: Princíp tvorby a funkcie prekladača jazyka definovaného pomocou programov LEX a YACC

Spomenuté možnosti a využitie modulu ply.py a teda programov LEX a YACC nevysvetľuje všetky možnosti využitia ktoré tieto programy ponúkajú. Ide o zhrnutie využitých vlastností počas vývoja kompilátora. Podrobnejšie informácie nájdete v literatúre [9] a [10].

Generovanie strojového kódu

Vstupom generátora strojového kódu je výstupná sekvencia parseru jazyka ST. Generátor identifikuje prvky sekvencie podľa Id, ktoré je prvým prvkom každého člena sekvencie.


[('LD', ('P', 0, 0)), ('LPS', None), ('LD', ('P', 0, 5)), ('O', ('P', 0, 7)), ('LD', ('P', 2, 1)), ('O', ('P', 1, 3)), ('=', ('P', 6, 0)), ('=', ('P', 1, 1)), ('A', ('P', 1, 0)), ('=', ('P', 3, 0))]
Ukážka 39: Výstupná sekvencia parseru


Druhým prvkom je parameter funkcie, ktorá sa zavolá podľa Id. Týmto parametrom býva najčastejšie adresa PLC. Pre generovanie strojového kódu je nezbytné aby tejto adrese odpovedala adresa mikrokontroléru. Hodnota adresy PLC je rôzna, pre rôzne mikrokontroléry na ktorých je PLC postavené. Tieto adresy sú definované v konfiguračnom súbore aktualizovanom pred samotným generovaním.


 def cmd_LD(adr):
    """ Prikaz jazyka STL - LD adr"""
    return adr_htoi(['00','91'])+adr_FlipSplit(adr_ADR(adr))+    [adr[2]]+adr_htoi(['FB','00','27','00','F9','0F','93'])


Ukážka 40: Definovanie funkcie generátora

Z ukážky je zrejmé ako je definovaný generátor strojového kódu, konkrétne príkazu LD. Parameter adr je adresa bitovej premennej. Návratovou hodnotou je list strojových kódov. Funkcie adr_htoi prevedú list čísel v hexadecimálnej sústave na list typu int. Funkcia adr_ADR prevádza adresu PLC na adresu mikrokontroléra a funkcia adr_FlipSplit rozdelí 16 bitovú adresu na dve 8 bitové a tie medzi sebou zamení.

Prevod programu LAD do STL

Program jazyka LAD je dátovo reprezentovaný ako matica zoznamov predstavujúcich jednotlivé LAD symboly.


[ 
[False,“LD”,['V',100,1],False],[False,“LD”,['V',200,1],False], ..   
[False,“LD”,['V',101,0],False],[False,“LD”,['V',201,0],False], ..   
..
]
Ukážka 41: LAD program

Elementom takejto matice je teda zoznam. Jeho obsah je definovaný ako:

  1. element – Udáva či je objekt vertikálne prepojený s objektom v riadku pod ním
  2. element – Identifikátor príkazu
  3. element – Parametre. Ich počet je variabilný a závisí od príkazu.
    n. element - Posledný element predstavuje logicky stav v ktorom sa LAD objekt nachádza. Je dôležitý iba počas simulácie

Pri prevode program prechádza po riadkoch maticu LAD a ukladá do poľa blokov všetkých sériovo usporiadaných objektov. Ak je program prepojený s riadkom pod ním, je celá prepojená skupina uložená ako jeden blok. Tento blok je vnútorne rozdelený na jednotlivé paralelné vetvy.

Obrázok 3: Princíp blokového rozdelenia LAD programu


Rozdeľovanie na paralelné vetvy sa vykonáva rekurzívne a preto môžu vzniknúť mnohonásobné vnorené štruktúry. Takto rozdelené pole je už možné jednoduchým spôsobom previesť na STL kód, prechádzaním elementov poľa.