#
# train controller based on rp2040 RPi Pico board with micropython rev. 1.20
#
# functionality:
# controls direction and speed of a model train
# does control two independent tracks
# gives status indication to user with LEDs for direction, stop, overlead
#
# all objects related to hardware are defined in module cfg.py.
# If you use different Pins or interfaces for your implementation adjust cfg.py accordingly
#
#
#
# by wolf2810
# initial rev. 3-Oct-2023
# 6-Oct-2023 : implementation of overload management
#
from track_controller import Track_control
import cfg
import time

#
if cfg.debug == 1:
    import print_status


# instantiate track controller's rails
rail1 = Track_control(cfg.R1Dir, cfg.R1Enable, cfg.R1Ovl, cfg.R1OLreset)
rail2 = Track_control(cfg.R2Dir, cfg.R2Enable, cfg.R2Ovl, cfg.R2OLreset)

# define and initialize power supplies
pws = cfg.track_power
rail1_pws = 0    # power supply 0 for rail1
rail2_pws = 1    # power supply 1 for rail2

pws.write(0, rail1_pws)
pws.write(0, rail2_pws)

if cfg.debug > 1:
    print("track controller for rail1 and rail2 initialized, power off")

OL_counter1 = 0     # loop counter for rail1 overload condition
OL_counter2 = 0     # loop counter for rail2 overload condition


# define functions


def speed_input(rail_pot):
    # read potentiometer and return values for setting the track power and direction
    # function expects and ADC object
    #
    pot_value = rail_pot.read_u16()

    # determine direction and speed. Map to 0-255 range of rail power supply.

    if pot_value in range(cfg.stop_lower, cfg.stop_upper):
        # value is in dead band around mid point

        if cfg.debug > 1:
            print("speed= 0,    direction= stop")

        return(0, "stop")

    if pot_value > cfg.stop_upper:
        # to the right of mid point, direction forward

        speed = int((255-cfg.min_speed) * (pot_value-cfg.stop_upper) / (cfg.ADCmax - cfg.stop_upper)) + cfg.min_speed

        if cfg.debug > 1:
            print("speed= ", int(speed), "    direction= forward")

        return(speed, "forward")

    if pot_value < cfg.stop_lower:
        # to the left of mid point, direction reverse

        speed = int((255-cfg.min_speed) * (cfg.stop_lower-pot_value) / cfg.stop_lower) + cfg.min_speed

        if cfg.debug > 1:
            print("speed= ", int(speed), "    direction= reverse")
        return(speed, "reverse")


def toggle(LED):
    # function to toggle state of an LED
    # expects a Pin object
    if LED.value():
        # print("LED off")
        LED.value(0)
    elif not LED.value():
        LED.value(1)
        # print("LED on")


# start of program


try:

    # wait for both pots to be in stop position for safe startup

    speed1, rail1_dir = speed_input(cfg.rail1_pot)
    speed2, rail2_dir = speed_input(cfg.rail2_pot)

    while rail1_dir != "stop":

        if cfg.debug > 0:
            print("rail 1 potentiometer not on stop position", end="")
            print(".", end="")
        toggle(cfg.rail1_LED)
        time.sleep_ms(500)
        speed1, rail1_dir = speed_input(cfg.rail1_pot)

    while rail2_dir != "stop":

        if cfg.debug > 1:
            print("rail 2 potentiometer not on stop position", end="\r")
        toggle(cfg.rail2_LED)
        time.sleep_ms(500)
        speed2, rail2_dir = speed_input(cfg.rail2_pot)

    # Both Potentiometers on stop position, start main loop
    cfg.rail1_LED.value(1)
    cfg.rail1_status["direction"] = "stop"
    cfg.rail2_LED.value(1)
    cfg.rail2_status["direction"] = "stop"

    # start of main loop
    if cfg.debug == 1:
        # clear terminal to enable status printout at top left position
        print_status.clear_terminal()

    #
    # start of control loop
    #
    while True:
        #
        # control rail 1
        #

        # read speed and direction
        speed, rail_dir = speed_input(cfg.rail1_pot)

        # check for overload condition
        if rail1.OL_test() is True:
            #
            if cfg.debug > 1:
                print("rail 1 overload detected", end="\r")

            # switch off rail power
            pws.write(0, rail1_pws)
            rail1.enable(False)

            # update status
            cfg.rail1_status["power"] = "off"
            cfg.rail1_status["overload"] = True
            cfg.rail1_status["speed"] = 0

            if cfg.debug == 1:
                print_status.print_rail_status()

            # wait for stop position
            if cfg.rail1_status["direction"] != "stop":

                OL_counter1 += 1

                if OL_counter1 > 20:
                    toggle(cfg.rail1_LED)
                    OL_counter1 = 0

            if rail_dir == "stop":
                # in stop position, reset overload condition now
                cfg.rail1_status["direction"] = "stop"

                resetted = rail1.OL_reset()

                if resetted is True:
                    # overload condition removed
                    cfg.rail1_status["overload"] = False
                    OL_counter1 = 0     # reset loop counter

                    if cfg.debug > 1:
                        print("rail 1 overload condition removed", end="\r")
                else:
                    # overload condition prevails
                    if cfg.debug > 1:
                        print("rail 1 overload condition prevails", end="\r")
        else:
            # no overload condition

            if rail_dir == "stop":
                # stop the train and update status

                pws.write(0, rail1_pws)     # power supply off
                rail1.enable(False)     # switch off track power
                cfg.rail1_status["direction"] = "stop"
                cfg.rail1_status["power"] = "off"
                cfg.rail1_status["speed"] = 0
                cfg.rail1_LED.value(1)    # STOP LED on

                if cfg.debug > 1:
                    print("rail 1 STOP position                      ", end="\r")
            else:
                # run the train

                rail1.direction(rail_dir)
                rail1.enable(True)     # track power on
                cfg.rail1_status["power"] = "on"
                cfg.rail1_LED.value(0)    # "Stop" LED off

                # check if train was at stop
                if cfg.rail1_status["direction"] == "stop":
                    # set power supply to break free power and wait to allow train starts moving
                    pws.write(cfg.break_free, rail1_pws)
                    time.sleep_ms(cfg.break_free_time)
                    cfg.rail1_status["speed"] = cfg.break_free
                    # update status direction
                    cfg.rail1_status["direction"] = rail_dir

                    if cfg.debug > 1:
                        print("rail 1 speed is ", str(speed), "     direction is ", rail_dir, "     ", end="\r")
                else:
                    pws.write(speed, rail1_pws)
                    cfg.rail1_status["speed"] = speed

                # update status direction
                cfg.rail1_status["direction"] = rail_dir

                if cfg.debug > 1:
                    print("rail 1 speed is ", str(speed), "     direction is ", rail_dir, "     ", end="\r")

        #
        # control rail 2
        #

        # read speed and direction
        speed, rail_dir = speed_input(cfg.rail2_pot)

        # check for overload condition
        if rail2.OL_test() is True:
            #
            if cfg.debug > 1:
                print("rail 2 overload detected", end="\r")

            # switch off rail power
            rail2.enable(False)
            pws.write(0, rail2_pws)

            # update status
            cfg.rail2_status["power"] = "off"
            cfg.rail2_status["overload"] = True
            cfg.rail2_status["speed"] = 0

            if cfg.debug == 1:
                print_status.print_rail_status()

            # wait for stop position
            if cfg.rail2_status["direction"] != "stop":

                OL_counter2 += 1

                if OL_counter2 > 50:
                    toggle(cfg.rail2_LED)
                    OL_counter2 = 0

            if rail_dir == "stop":
                # in stop position, reset overload condition now
                cfg.rail2_status["direction"] = "stop"

                resetted = rail2.OL_reset()

                if resetted is True:
                    # overload condition removed
                    cfg.rail2_status["overload"] = False
                    OL_counter2 = 0     # reset loop counter

                    if cfg.debug > 1:
                        print("rail 2 overload condition removed", end="\r")
                else:
                    # overload condition prevails
                    if cfg.debug > 1:
                        print("rail 2 overload condition prevails", end="\r")
        else:
            # no overload condition

            if rail_dir == "stop":
                # stop the train and update status

                pws.write(0, rail2_pws)     # power supply off
                rail2.enable(False)     # switch off track power
                cfg.rail2_status["direction"] = "stop"
                cfg.rail2_status["power"] = "off"
                cfg.rail2_status["speed"] = 0
                cfg.rail2_LED.value(1)    # STOP LED on

                if cfg.debug > 1:
                    print("rail 2 STOP position                      ", end="\r")
            else:
                # run the train

                rail2.direction(rail_dir)
                rail2.enable(True)     # track power on
                cfg.rail2_status["power"] = "on"
                cfg.rail2_LED.value(0)    # "Stop" LED off

                # check if train was at stop
                if cfg.rail2_status["direction"] == "stop":
                    # set power supply to break free power and wait to allow train starts moving
                    pws.write(cfg.break_free, rail2_pws)
                    time.sleep_ms(cfg.break_free_time)
                    cfg.rail2_status["speed"] = cfg.break_free
                    # update status direction
                    cfg.rail2_status["direction"] = rail_dir

                    if cfg.debug > 1:
                        print("rail 2 speed is ", str(speed), "     direction is ", rail_dir, "     ", end="\r")
                else:
                    pws.write(speed, rail2_pws)
                    cfg.rail2_status["speed"] = speed

                # update status direction
                cfg.rail2_status["direction"] = rail_dir

                if cfg.debug > 1:
                    print("rail 2 speed is ", str(speed), "     direction is ", rail_dir, "     ", end="\r")

        if cfg.debug == 1:
            print_status.print_rail_status()

        # slow down control loop, to enable easier debugging with debug=2
        if cfg.debug > 1:
            # input("press Enter to continue")
            time.sleep_ms(500)
    #
    # End of control loop
    #

except KeyboardInterrupt:
    print("\n\nGot Ctrl-C exiting program")

except ValueError as error:
    print(error)

finally:
    # pws.shut_down(2)
    rail1.enable(False)
    rail2.enable(False)
    if cfg.debug:
        print("\nfinally: disabled rail power, exiting ...")
