Source

BASIC-RoBots / src / robot / robotsos.py

##
## os.py for BASIC-RoBots
## 
## Copyright (C) 2012 Pierre Surply
## <pierre.surply@gmail.com>
##
## This file is part of BASIC-RoBots.
##
##    BASIC-RoBots is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    BASIC-RoBots is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with BASIC-RoBots.  If not, see <http://www.gnu.org/licenses/>.
##  
## Started on  Wed May  9 22:56:00 2012 Pierre Surply
## Last update Wed Sep  5 17:02:21 2012 Pierre Surply
##

import os
import shutil
from pygame.locals import *

os.chdir('src/robot')
import terminal
os.chdir('../../')
import basic

class Command:
    def __init__(self, name, param, description, function):
        self.name = name
        self.param = param
        self.description = description
        self.function = function

    def apply(self, os, arg):
        self.function(os, arg)

class RoBotsOS:
    def __init__(self, robot, path, mem, events):
        self.robot = robot
        self.current_dir = "/"
        self.path = path
        self.name = robot.name
        self.ext_cmd = robot.ext_cmd
        self.mem = mem
        self.terminal = terminal.Terminal(events)
        self.terminal.set_title(self.name)
        self.set_cmds([("help", "", "shows help", lambda o, a:o.help(a)),\
                           ("info", "", "shows some information about the robot", lambda o, a:o.info(a)),\
                           ("intcmd", "", "shows internal commands", lambda o, a: o.show_int_cmd(a)),\
                           ("extcmd", "", "shows external commands", lambda o, a: o.show_ext_cmd(a)),\
                           ("welcome", "", "prints welcome message", lambda o, a: o.welcome(a)),\
                           ("pwd", "", "prints name of current directory", lambda o, a: o.pwd(a)),\
                           ("ls", "", "lists directory contents", lambda o, a: o.ls(a)),\
                           ("mkdir", "DIRECTORY", "makes directory", lambda o, a: o.mkdir(a)),\
                           ("touch", "FILE", "makes file", lambda o, a: o.touch(a)),\
                           ("cd", "[DIRECTORY]", "changes current directory", lambda o, a: o.cd(a)),\
                           ("cat", "FILE", "prints file", lambda o, a: o.cat(a)),\
                           ("run", "FILE", "executes a BASIC script", lambda o, a: o.execute(a, False)),\
                           ("debug", "FILE", "debugs a BASIC script", lambda o, a: o.execute(a, True)),\
                           ("edit", "FILE", "edit a file", lambda o, a: o.edit(a)),\
                           ("inv", "", "lists inventory", lambda o, a: o.ls_inv(a)),\
                           ("let", "REG VALUE", "sets register value", lambda o, a: o.let(a)),\
                           ("stack", "", "prints stack", lambda o, a: o.stack(a)),\
                           ("tree", "", "lists directory contents recursively", lambda o, a: o.tree(a)),\
                           ("pulllib", "", "Gets the standard library in /lib ", lambda o, a: o.pulllib()),\
                           ("pushlib", "", "Sets the directory /lib as standard library", lambda o, a: o.pushlib()),\
                           ("clear", "", "clears screen", lambda o, a: o.clear(a))])
        if self.robot.energy > 0:
            self.welcome(None)
        self.start_prompt()
        self.task = 0

    def get_path(self, path):
        return self.path + path

    def start_prompt(self):
        self.terminal.start_prompt(self.current_dir.split("/")[-2] + "\xaf")

    def set_cmds(self, cmds):
        self.cmds = []
        for i in cmds:
            self.cmds.append(Command(i[0], i[1], i[2], i[3]))

    def update(self, selected, display, events):
        w = display.window.get_width()/16-1
        h = display.window.get_height()/12-1
        if self.robot.energy > 0:
            if self.task == 0 and selected:
                cmd = self.terminal.prompt()
                if cmd != None:
                    for i in cmd.split("&&"):
                        i = i.strip()
                        if i != "":
                            self.command(i)
                    if self.task == 0 and self.robot.energy > 0:
                        self.start_prompt()
                info = (self.mem, 8)
            elif self.task == 1:
                if self.basic.debug:
                    self.basic.print_debug()
                    if events.get_key_once(K_r):
                        self.basic.debug = False
                        self.terminal.clear()
                    if events.get_key_once(K_n) or self.terminal.act_prompt:
                        b = self.basic.eval_line()
                    else:
                        b = True
                else:
                    b = self.basic.eval_line()
                if not b:
                    self.task = 0
                    if self.robot.energy > 0:
                        self.start_prompt()
                info = (self.mem, 8)
            elif self.task == 2 and selected:
                if not self.terminal.edit(self, h):
                    self.task = 0
                    self.start_prompt()
                info = ({"CTRL+S" : "Save", \
                             "CTRL+C" : "Close"}, 15)
        else:
            if selected:
                while len(events.char_stack) > 0:
                    events.char_stack.popleft()
            info = None
        if selected:
            self.terminal.display(display, (0, 0, w, h), info, ({"SH" : self.robot.shield,\
                                                                     "EN" : self.robot.energy}, 8))

    def command(self, cmd):
        cmd = cmd.strip().split(" ")
        found = False
        for i in self.cmds:
            if cmd[0] == i.name:
                i.apply(self, cmd[1:])
                found = True
        if not found:
            if cmd[0] in self.ext_cmd:
                ret = self.robot.run_ext_cmd(self.ext_cmd[cmd[0]][0], \
                                           self.ext_cmd[cmd[0]][3])
                if ret != None:
                    self.terminal.write_line(cmd[0] + "\x1a " + str(ret))
                found = True
        if not found and cmd[0] != "":
            self.terminal.write_line("'" + cmd[0] + "' : command not found")

    def info(self, arg):
        self.terminal.write("--- Informations ---\n", 1)
        self.terminal.write("Name", 1)
        self.terminal.write_line(": " + self.name)
        self.terminal.write("X", 1)
        self.terminal.write(": " + str(self.robot.pos_x) + "  ")
        self.terminal.write("Y", 1)
        self.terminal.write(": " + str(self.robot.pos_y) + "  ")
        self.terminal.write("Or", 1)
        self.terminal.write_line(": " + ["North", "East", "South", "West"][self.robot.orient])
        self.terminal.write("Shield", 1)
        self.terminal.write_line(": " + hex(self.robot.shield) + "/0xff ("+\
                                     str((self.robot.shield * 100) / 0xFF)+"%)")
        self.terminal.write("Energy", 1)
        self.terminal.write_line(": " + hex(self.robot.energy) + "/0xff ("+\
                                     str((self.robot.energy * 100) / 0xFF)+"%)")
        self.terminal.write_line("Registers", 1)
        for i in sorted(self.robot.mem.keys()):
            self.terminal.write("  ")
            self.terminal.write(i, 1)
            self.terminal.write(": " + hex(self.robot.mem[i]))
        self.terminal.write_line("")        

    def help(self, arg):
        if len(arg) > 0:
            if arg[0] in self.ext_cmd:
                self.help_cmd(arg[0], True, self.ext_cmd[arg[0]][1], \
                                  self.ext_cmd[arg[0]][4], \
                                  self.ext_cmd[arg[0]][3], \
                                  self.ext_cmd[arg[0]][2])
            else:
                for i in self.cmds:
                    if arg[0] == i.name:
                        self.help_cmd(i.name, False, i.description, i.param)
        else:
            self.terminal.write_line("-- Help --", 1)
            self.terminal.write("intcmd", 1)
            self.terminal.write_line(": shows internal commands")
            self.terminal.write("extcmd", 1)
            self.terminal.write_line(": shows external commands")

    def help_cmd(self, name, ext, desc, arg, cost=None, ret=None):
        self.terminal.write("Command", 1)
        self.terminal.write_line(": " + name)
        self.terminal.write("Type", 1)
        if ext:
            self.terminal.write_line(": external command")
        else:
            self.terminal.write_line(": internal command")
            self.terminal.write("Argument(s)", 1)
            self.terminal.write_line(": " + arg)
        self.terminal.write("Description", 1)
        self.terminal.write_line(": " + desc)
        if ext:
            self.terminal.write("Argument(s)", 1)
            if arg == {}:
                self.terminal.write_line(": -")
            else:
                self.terminal.write(":\n")
                for i in arg.keys():                    
                    self.terminal.write_line("  \x10 " + i + ": " + arg[i])
            self.terminal.write("Return", 1)
            self.terminal.write_line(": " + ret)
            self.terminal.write("Cost", 1)
            self.terminal.write_line(": " + str(cost[0]) + " EN")

    def show_int_cmd(self, arg):
        self.terminal.write("--- Internal commands ---\n", 1)
        for i in self.cmds:
            self.terminal.write(i.name, 1)
            self.terminal.write(" " + i.param + ": " + i.description + "\n")

    def show_ext_cmd(self, arg):
        self.terminal.write("--- External commands ---\n", 1)
        for i in sorted(self.ext_cmd.keys()):
            self.terminal.write(i, 1)
            self.terminal.write(": " + self.ext_cmd[i][1] + "\n")
                
    def welcome(self, arg):
        self.terminal.write_line("\x04\x04\x04 " + self.name +  " \x04\x04\x04\nType 'help' for more information")

    def pwd(self, arg):
        self.terminal.write_line(self.current_dir)

    def ls(self, arg):
        for i in os.listdir(self.get_path(self.current_dir)):
            if os.path.isdir(self.get_path(self.current_dir + i)):
                self.terminal.write(i, 4)
            else:
                self.terminal.write(i)
            self.terminal.write(" ")
        self.terminal.write_line("")

    def mkdir(self, arg):
        if len(arg) > 0:
            os.mkdir(self.get_path(self.current_dir + arg[0]))

    def touch(self, arg):
        if len(arg) > 0:
            path = self.get_path(self.current_dir + arg[0])
            if not os.path.exists(path): 
                open(path, 'w').close() 
    
    def cd(self, arg):
        directory = "/"
        if len(arg) > 0:
            if arg[0][0] == "/":
                directory = arg[0]
            elif arg[0] == "..":
                if len(self.current_dir) > 2:
                    directory = "/".join(self.current_dir.split("/")[:-2])
            else:
                directory = self.current_dir+arg[0]
        if os.path.exists(self.get_path(directory)):
            if os.path.isdir(self.get_path(directory)):
                self.current_dir = directory
                if len(self.current_dir) == 0 or self.current_dir[-1] != "/":
                    self.current_dir += "/"
            else:
                self.terminal.write_line(arg[0] + ": Not a directory")
        else:
            self.terminal.write_line(arg[0] + ": No such file or directory")
        
    def clear(self, arg):
        self.terminal.clear()

    def cat(self, arg):
        if len(arg) > 0:
            if os.path.isfile(self.get_path(self.current_dir + arg[0])):
                self.terminal.write_line(open(self.get_path(self.current_dir + arg[0]), 'r').read())
            else:
                self.terminal.write_line(arg[0] + ": No such file or directory")

    def execute(self, arg, debug):
        if len(arg) > 0:
            if os.path.isfile(self.get_path(self.current_dir + arg[0])):
                f = open(self.get_path(self.current_dir) + arg[0], 'r')
                self.basic = basic.Basic(self.get_path(self.current_dir),\
                                             f.read(), \
                                             self.robot,\
                                             self.terminal, \
                                             debug)
                f.close()
                self.terminal.write_line("Running " + arg[0] + "...\nPress CTRL+C to interrupt")
                self.task = 1
            else:
                self.terminal.write_line(arg[0] + ": No such file or directory")
        else:
            self.terminal.write_line("run: No input file")

    def edit(self, arg):
        if len(arg) > 0:
            if os.path.isfile(self.get_path(self.current_dir + arg[0])):
                self.terminal.start_edit(self.current_dir + arg[0], \
                                             open(self.get_path(self.current_dir+arg[0])).read())
                self.task = 2
            else:
                self.terminal.write_line(arg[0] + ": No such file or directory")
        else:
            self.terminal.write_line("edit: No input file")

    def save_file(self, path, s):
        f = open(self.get_path(path), 'w')
        f.write("\n".join(s))

    def ls_inv(self, arg):
        if self.robot.inv.weight > 0:
            self.terminal.write("Id", 1)
            self.terminal.write("  ")
            self.terminal.write("Item", 1)
            self.terminal.write("        ")
            self.terminal.write("Qty\n", 1)
            self.terminal.write_line(str(self.robot.inv))
        else:
            self.terminal.write("\n --Empty--\n")
        self.terminal.write("\nCapacity", 1)
        self.terminal.write_line(": " + str(self.robot.inv.capacity) + " g")
        self.terminal.write("Weight", 1)
        self.terminal.write_line(": " + str(self.robot.inv.weight) + " g")
        self.terminal.write("Free", 1)
        f = self.robot.inv.capacity - self.robot.inv.weight
        self.terminal.write(": ")
        if f > 0:
            self.terminal.write(str(f))
        else:
            self.terminal.write(str(f), 6)
        self.terminal.write(" g (" + str((f*100)/self.robot.inv.capacity) + " %)\n")

    def let(self, arg):
        self.basic = basic.Basic(self.get_path(self.current_dir),\
                                     "LET " + " ".join(arg), \
                                     self.robot,\
                                     self.terminal)
        self.task = 1

    def stack(self, arg):
        if self.robot.mem_stack == []:
            self.terminal.write_line("The stack is empty")
        else:
            for i in self.robot.mem_stack:
                self.terminal.write("\xB3" + hex(i))
            self.terminal.write_line(" \x1B")

    def tree(self, arg):
        self.rec_tree(True, 0, self.get_path(self.current_dir))

    def rec_tree(self, last, dec, path):
        self.terminal.write_line(path.split("/")[-2], 4)
        if last:
            c = "   "
        else:
            c = "\xb3  "
        l = os.listdir(path)
        if len(l) > 0:
            for i in range(len(l)-1):
                if os.path.isdir(path+l[i]):
                    self.terminal.write(c * dec + "\xc3\xc4\xc4")
                    self.rec_tree(False, dec+1, path+l[i]+"/")
                else:
                    self.terminal.write_line(c * dec + "\xc3\xc4\xc4" + l[i])
            if os.path.isdir(path+l[-1]):
                self.terminal.write(c * dec + "\xc0\xc4\xc4")
                self.rec_tree(True, dec+1, path+l[-1]+"/")
            else:
                self.terminal.write_line(c * dec + "\xc0\xc4\xc4" + l[-1])

    def pulllib(self):
        if os.path.isdir(self.get_path("/lib")):
            shutil.rmtree(self.get_path("/lib"))
        shutil.copytree("saves/" + self.robot.world + "/lib", self.get_path("/lib"))
        self.terminal.write_line("Pulled library")

    def pushlib(self):
        if os.path.isdir(self.get_path("/lib")):
            shutil.rmtree("saves/" + self.robot.world + "/lib")
            shutil.copytree(self.get_path("/lib"), "saves/" + self.robot.world + "/lib")
            self.terminal.write_line("Pushed library")