Limitacions del transpilador Python

Simulador IoT-Vertebrae · jordibinefa.github.io/iotv


El simulador web executa codi Python que escrius directament al navegador. Per fer-ho possible, un transpilador converteix el teu codi Python a JavaScript abans d'executar-lo. Aquesta conversió no és perfecta: hi ha construccions de Python que funcionen normalment en un ordinador però que el simulador no sap convertir.

Aquesta pàgina explica exactament què pots i no pots fer, amb exemples de com reescriure el codi per que funcioni.


Mòduls disponibles

Al simulador no existeix import. No pots importar cap mòdul extern. Els únics objectes disponibles automàticament (sense cap import) són:

Disponible Descripció
iotv Control de vèrtebres digitals i analògiques
time Només time.sleep(s)
math Funcions matemàtiques bàsiques (math.sqrt, math.sin, etc.)
random Números aleatoris (random.random(), random.randint())
PLCMemory Memòria clau-valor persistent entre execucions

Qualsevol altra línia import (incloent import iotv o import time) generarà un error o simplement s'ignorarà.

# ❌ NO funciona al simulador
import iotv
import time
import math

# ✅ Correcte — res d'imports, tot ja està disponible
while True:
    v = iotv.ain2v(iotv.ain("0x0", "b", 1))
    time.sleep(0.1)

Operadors lògics: and, or, not

Els operadors lògics and, or i not no funcionen al transpilador. Si els uses, el programa es comportarà de manera inesperada o donarà error.

Solució: substitueix cada condició composta per if niuats.

# ❌ NO funciona
if r == 1 and g == 1 and b == 1:
    iotv.doutbit("0x0", "a", 3, 1)

# ✅ Correcte — if niuats
if r == 1:
    if g == 1:
        if b == 1:
            iotv.doutbit("0x0", "a", 3, 1)
# ❌ NO funciona
if emergencia == 1 or alarma == 1:
    aturar_motor()

# ✅ Correcte — comprova cada condició per separat
if emergencia == 1:
    aturar_motor()
if alarma == 1:
    aturar_motor()

Classes (class)

La definició de classes amb class no és suportada.

# ❌ NO funciona
class Motor:
    def __init__(self, addr):
        self.addr = addr
    def encendre(self):
        iotv.doutbit(self.addr, "a", 0, 1)

Solució: usa funcions simples de primer nivell (def) i passa les variables com a paràmetres.

# ✅ Correcte
def encendre_motor(addr):
    iotv.doutbit(addr, "a", 0, 1)

def aturar_motor(addr):
    iotv.doutbit(addr, "a", 0, 0)

encendre_motor("0x0")

Funcions niuades i closures

Pots definir funcions amb def, però sempre al primer nivell del programa — mai dins d'una altra funció.

# ❌ NO funciona — funció niuada
def gestionar_motor():
    def encendre():          # ← niuada dins gestionar_motor
        iotv.doutbit("0x0", "a", 0, 1)
    encendre()

# ✅ Correcte — totes les funcions al primer nivell
def encendre():
    iotv.doutbit("0x0", "a", 0, 1)

def gestionar_motor():
    encendre()

List comprehensions

Les expressions de llista en una línia (list comprehensions) no funcionen.

# ❌ NO funciona
valors = [iotv.ain("0x0", "b", ch) for ch in range(1, 5)]

# ✅ Correcte — bucle for explícit
valors = []
for ch in range(1, 5):
    valors.append(iotv.ain("0x0", "b", ch))

f-strings amb expressions

Les f-strings simples poden funcionar en alguns casos, però les que contenen expressions o càlculs fallen. És més segur usar concatenació de strings o print() amb múltiples arguments.

# ❌ Arriscat — pot fallar
print(f"Temperatura: {int(temp * 10) / 10} °C")

# ✅ Correcte — múltiples arguments a print
print("Temperatura:", int(temp * 10) / 10, "°C")

# ✅ Correcte — concatenació amb str()
print("Temperatura: " + str(int(temp * 10) / 10) + " °C")

Excepcions (try / except / finally)

Els blocs de gestió d'errors no funcionen al simulador.

# ❌ NO funciona
try:
    valor = iotv.ain("0x0", "b", 1)
except:
    valor = 0

# ✅ Correcte — comprova el valor directament
valor = iotv.ain("0x0", "b", 1)
if valor == "Error":
    valor = 0

lambda, *args, **kwargs

Les funcions anònimes lambda i els arguments variables no estan suportats.

# ❌ NO funciona
doble = lambda x: x * 2

# ✅ Correcte — funció normal
def doble(x):
    return x * 2

global i nonlocal

Les paraules clau global i nonlocal no funcionen. Totes les variables del programa es comparteixen automàticament entre funcions al simulador.

# ❌ NO funciona (i no cal)
comptador = 0

def incrementar():
    global comptador      # ← el transpilador no ho suporta
    comptador = comptador + 1

# ✅ Correcte — usa la variable directament, sense global
comptador = 0

def incrementar():
    comptador = comptador + 1   # funciona perquè les vars són globals al sim

yield i generadors

Les funcions generadores amb yield no estan suportades.


Arrodoniment: evita round()

La funció round() pot tenir comportaments inesperats al transpilador. Per arrodonir a N decimals usa la divisió entera:

# ❌ Pot donar resultats estranys
valor = round(temperatura, 1)

# ✅ Correcte — arrodoniment manual a 1 decimal
valor = int(temperatura * 10) / 10

# ✅ Correcte — arrodoniment a enter
valor = int(temperatura)

Format d'adreces

Al simulador s'usa el format hexadecimal en string: "0x0", "0x1", "0x2"...

A la Raspberry Pi real s'usa el format binari en string: '0000', '0001', '0010'...

# Simulador web
iotv.dinbit("0x0", "b", 0)

# Raspberry Pi real (can_iotv_v2_1)
iotv.dinbit('0000', 'B', 0)

Resum ràpid

Construcció Simulador RPi real
import ❌ no cal (ja tot disponible) ✅ necessari
and / or ❌ usa if niuats ✅ funciona
class
def primer nivell
def niuada
List comprehension ❌ usa for explícit
try / except ❌ usa if
f-string simple ⚠ arriscat
f-string amb càlcul ❌ usa print(a, b, c)
lambda
global / nonlocal ❌ no cal ✅ necessari
yield
round() ⚠ usa int(x*10)/10
Adreça vèrtebra "0x0" '0000'