aboutsummaryrefslogtreecommitdiff
path: root/bin/printer.py
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2024-02-29 02:29:14 +0100
committerPaul Oliver <contact@pauloliver.dev>2024-02-29 02:29:14 +0100
commit2250b4db92bd272dbb1fd717eb791e293c17e37a (patch)
tree0bde7ed8a2ba8b2a04da629b0317568dea2a6b3c /bin/printer.py
parentde427d319c699b8bed7ed73289b3698f13ac3acc (diff)
Store python modules on './bin/modules/' subdirectory. [#37]
Diffstat (limited to 'bin/printer.py')
-rw-r--r--bin/printer.py858
1 files changed, 0 insertions, 858 deletions
diff --git a/bin/printer.py b/bin/printer.py
deleted file mode 100644
index 1fca43d..0000000
--- a/bin/printer.py
+++ /dev/null
@@ -1,858 +0,0 @@
-""" SALIS: Viewer/controller for the SALIS simulator.
-
-File: printer.py
-Author: Paul Oliver
-Email: paul.t.oliver.design@gmail.com
-
-This module should be considered the 'view' part of the Salis simulator. It
-takes care of displaying the simulator's state in a nicely formatted, intuitive
-format. It makes use of the curses library for terminal handling.
-"""
-
-import curses
-import curses.textpad
-import os
-import time
-
-from collections import OrderedDict
-from ctypes import c_uint8, c_uint32, cast, POINTER
-from handler import Handler
-from world import World
-
-
-class Printer:
- ESCAPE_KEY = 27
-
- def __init__(self, sim):
- """ Printer constructor. It takes care of starting up curses, defining
- the data pages and setting the printer on its initial state.
- """
- self.__sim = sim
- self.__color_pair_count = 0
-
- # Initialize curses screen, instruction and proc-element list before
- # other private elements that depend on them.
- self.screen = self.__get_screen()
- self.inst_list = self.__get_inst_list()
- self.proc_elements = self.__get_proc_elements()
-
- # We can now initialize all other privates.
- self.__main = self.__get_main()
- self.__pages = self.__get_pages()
- self.__minimal = self.__get_minimal()
- self.__main_scroll = 0
- self.__proc_element_scroll = 0
- self.__proc_gene_scroll = 0
- self.__proc_gene_view = False
- self.__curs_y = 0
- self.__curs_x = 0
- self.__print_hex = False
- self.size = self.screen.getmaxyx()
- self.current_page = "MEMORY"
- self.selected_proc = 0
- self.selected_proc_data = (c_uint32 * len(self.proc_elements))()
- self.proc_list_scroll = 0
- self.world = World(self, self.__sim)
-
- def __del__(self):
- """ Printer destructor exits curses.
- """
- curses.endwin()
-
- def get_color_pair(self, fg, bg=-1):
- """ We use this method to set new color pairs, keeping track of the
- number of pairs already set. We return the new color pair ID.
- """
- self.__color_pair_count += 1
- curses.init_pair(self.__color_pair_count, fg, bg)
- return self.__color_pair_count
-
- def get_cmd(self):
- """ This returns the pressed key from the curses handler. It's called
- during the simulation's main loop. Flushing input is important when in
- non-blocking mode.
- """
- ch = self.screen.getch()
- curses.flushinp()
- return ch
-
- def set_nodelay(self, nodelay):
- """ Toggles between blocking and non-blocking mode on curses.
- """
- self.screen.nodelay(nodelay)
-
- def toggle_hex(self):
- """ Toggle between decimal or hexadecimal printing of all simulation
- state elements.
- """
- self.__print_hex = not self.__print_hex
-
- def on_resize(self):
- """ Called whenever the terminal window gets resized.
- """
- self.size = self.screen.getmaxyx()
- self.scroll_main()
- self.world.zoom_reset()
-
- def flip_page(self, offset):
- """ Change data page by given offset (i.e. '1' for next page or '-1'
- for previous one).
- """
- pidx = list(self.__pages.keys()).index(self.current_page)
- pidx = (pidx + offset) % len(self.__pages)
- self.current_page = list(self.__pages.keys())[pidx]
- self.scroll_main()
-
- def scroll_main(self, offset=0):
- """ Scrolling is allowed whenever the current page does not fit inside
- the terminal window. This method gets called, with no offset, under
- certain situations, like changing pages, just to make sure the screen
- gets cleared and at least some of the data is always scrolled into
- view.
- """
- self.screen.clear()
- len_main = len(self.__main)
- len_page = len(self.__pages[self.current_page])
- max_scroll = (len_main + len_page + 5) - self.size[0]
- self.__main_scroll += offset
- self.__main_scroll = max(0, min(self.__main_scroll, max_scroll))
-
- def proc_scroll_left(self):
- """ Scroll process data elements or genomes (on PROCESS view) to the
- left.
- """
- if self.current_page == "PROCESS":
- if self.__proc_gene_view:
- self.__proc_gene_scroll -= 1
- self.__proc_gene_scroll = max(0, self.__proc_gene_scroll)
- else:
- self.__proc_element_scroll -= 1
- self.__proc_element_scroll = max(0, self.__proc_element_scroll)
-
- def proc_scroll_right(self):
- """ Scroll process data elements or genomes (on PROCESS view) to the
- right.
- """
- if self.current_page == "PROCESS":
- if self.__proc_gene_view:
- self.__proc_gene_scroll += 1
- else:
- self.__proc_element_scroll += 1
- max_scroll = len(self.proc_elements) - 1
- self.__proc_element_scroll = min(
- max_scroll, self.__proc_element_scroll
- )
-
- def proc_scroll_down(self, fast=False):
- """ Scroll process data table (on PROCESS view) up.
- """
- if self.current_page == "PROCESS":
- if fast:
- len_page = len(self.__main) + len(self.__pages["PROCESS"]) + 6
- scroll = max(0, self.size[0] - len_page)
- else:
- scroll = 1
-
- self.proc_list_scroll = max(0, self.proc_list_scroll - scroll)
-
- def proc_scroll_up(self, fast=False):
- """ Scroll process data table (on PROCESS view) down.
- """
- if self.current_page == "PROCESS":
- if fast:
- len_page = len(self.__main) + len(self.__pages["PROCESS"]) + 6
- scroll = max(0, self.size[0] - len_page)
- else:
- scroll = 1
-
- self.proc_list_scroll = min(
- self.__sim.lib.sal_proc_get_capacity() - 1,
- self.proc_list_scroll + scroll
- )
-
- def proc_scroll_to(self, proc_id):
- """ Scroll process data table (on PROCESS view) to a specific position.
- """
- if self.current_page == "PROCESS":
- if proc_id < self.__sim.lib.sal_proc_get_capacity():
- self.proc_list_scroll = proc_id
- else:
- raise RuntimeError("Error: scrolling to invalid process")
-
- def proc_scroll_vertical_reset(self):
- """ Scroll process data table (on PROCESS view) back to top.
- """
- if self.current_page == "PROCESS":
- self.proc_list_scroll = 0
-
- def proc_scroll_horizontal_reset(self):
- """ Scroll process data or genome table (on PROCESS view) back to the
- left.
- """
- if self.current_page == "PROCESS":
- if self.__proc_gene_view:
- self.__proc_gene_scroll = 0
- else:
- self.__proc_element_scroll = 0
-
- def proc_select_prev(self):
- """ Select previous process.
- """
- if self.current_page in ["PROCESS", "WORLD"]:
- self.selected_proc -= 1
- self.selected_proc %= self.__sim.lib.sal_proc_get_capacity()
-
- def proc_select_next(self):
- """ Select next process.
- """
- if self.current_page in ["PROCESS", "WORLD"]:
- self.selected_proc += 1
- self.selected_proc %= self.__sim.lib.sal_proc_get_capacity()
-
- def proc_select_first(self):
- """ Select first process on reaper queue.
- """
- if self.current_page in ["PROCESS", "WORLD"]:
- if self.__sim.lib.sal_proc_get_count():
- self.selected_proc = self.__sim.lib.sal_proc_get_first()
-
- def proc_select_last(self):
- """ Select last process on reaper queue.
- """
- if self.current_page in ["PROCESS", "WORLD"]:
- if self.__sim.lib.sal_proc_get_count():
- self.selected_proc = self.__sim.lib.sal_proc_get_last()
-
- def proc_select_by_id(self, proc_id):
- """ Select process from given ID.
- """
- if proc_id < self.__sim.lib.sal_proc_get_capacity():
- self.selected_proc = proc_id
- else:
- raise RuntimeError("Error: attempting to select non-existing proc")
-
- def proc_scroll_to_selected(self):
- """ Scroll WORLD or PROCESS page so that selected process becomes
- visible.
- """
- if self.current_page == "PROCESS":
- self.proc_list_scroll = self.selected_proc
- elif self.current_page == "WORLD":
- if not self.__sim.lib.sal_proc_is_free(self.selected_proc):
- index = self.proc_elements.index("mb1a")
- address = self.selected_proc_data[index]
- self.world.scroll_to(address)
-
- def proc_toggle_gene_view(self):
- """ Toggle between data element or genome view on PROCESS page.
- """
- if self.current_page == "PROCESS":
- self.__proc_gene_view = not self.__proc_gene_view
-
- def run_cursor(self):
- """ We can toggle a visible cursor on WORLD view to aid us in selecting
- processes.
- """
- if self.current_page == "WORLD" and self.size[1] > World.PADDING:
- curses.curs_set(True)
-
- while True:
- self.__curs_y = max(0, min(self.__curs_y, self.size[0] - 1))
- self.__curs_x = max(World.PADDING, min(
- self.__curs_x, self.size[1] - 1
- ))
- self.screen.move(self.__curs_y, self.__curs_x)
- cmd = self.screen.getch()
-
- if cmd in [ord("c"), curses.KEY_RESIZE, self.ESCAPE_KEY]:
- self.on_resize()
- break
- elif cmd == curses.KEY_LEFT:
- self.__curs_x -= 1
- elif cmd == curses.KEY_RIGHT:
- self.__curs_x += 1
- elif cmd == curses.KEY_DOWN:
- self.__curs_y += 1
- elif cmd == curses.KEY_UP:
- self.__curs_y -= 1
- elif cmd == ord("\n"):
- self.__proc_select_by_cursor()
- break
-
- curses.curs_set(False)
-
- def run_console(self):
- """ Run the Salis console. You can use the console to control all main
- aspects of the simulation, like compiling genomes into memory, creating
- or killing organisms, setting auto-save interval, among other stuff.
- """
- # Print a pythonic prompt.
- self.__print_line(self.size[0] - 1, ">>> ", scroll=False)
- self.screen.refresh()
-
- # Create the console child window. We turn it into a Textbox object in
- # order to allow line-editing and extract output easily.
- console = curses.newwin(1, self.size[1] - 5, self.size[0] - 1, 5)
- textbox = curses.textpad.Textbox(console, insert_mode=True)
- textbox.stripspaces = True
-
- # Grab a copy of the console history and instantiate a pointer to the
- # last element.
- history = self.__sim.handler.console_history + [""]
- pointer = len(history) - 1
-
- # Nested method reinserts recorded commands from history into console.
- def access_history(cmd):
- nonlocal pointer
-
- if pointer == len(history) - 1:
- history[-1] = console.instr().strip()
-
- if cmd == "up" and pointer != 0:
- pointer -= 1
- elif cmd == "down" and pointer < len(history) - 1:
- pointer += 1
-
- console.clear()
- console.addstr(0, 0, history[pointer])
- console.refresh()
-
- # Declare custom validator to control special commands.
- def validator(cmd):
- EXIT = 7
-
- if cmd in [curses.KEY_RESIZE, self.ESCAPE_KEY]:
- console.clear()
- return EXIT
- # Provide general code for back-space key, in case it's not
- # correctly defined.
- elif cmd in [127, curses.KEY_BACKSPACE]:
- return curses.KEY_BACKSPACE
- elif cmd == curses.KEY_UP:
- access_history("up")
- elif cmd == curses.KEY_DOWN:
- access_history("down")
- else:
- return cmd
-
- # Run the Textbox object with our custom validator.
- curses.curs_set(True)
- output = textbox.edit(validator)
- curses.curs_set(False)
-
- # Finally, extract data from console and send to handler. Respond to
- # any possible resize event here.
- self.__sim.handler.handle_console(output)
- self.screen.clear()
- self.on_resize()
-
- def show_console_error(self, message):
- """ Shows Salis console error messages, if any. These messages might
- contain actual python exception output.
- """
- self.__print_line(self.size[0] - 1, ">>>", curses.color_pair(
- self.__pair_error
- ) | curses.A_BOLD)
- self.screen.refresh()
-
- # We also use a Textbox object, just so that execution gets halted
- # until a key gets pressed (even on non-blocking mode).
- console = curses.newwin(1, self.size[1] - 5, self.size[0] - 1, 5)
- textbox = curses.textpad.Textbox(console)
-
- # Curses may raise an exception if printing on the edge of the screen;
- # we can just ignore it.
- try:
- console.addstr(0, 0, message, curses.color_pair(
- self.__pair_error
- ) | curses.A_BOLD)
- except curses.error:
- pass
-
- # Custom validator simply exits on any key.
- def validator(cmd):
- EXIT = 7
- return EXIT
-
- textbox.edit(validator)
- self.screen.clear()
- self.on_resize()
-
- def print_page(self):
- """ Print current page to screen. We use the previously generated
- '__pages' dictionary to easily associate a label to a Salis function.
- """
- # If in minimal mode, print only minial widget.
- if self.__sim.minimal:
- self.__print_minimal()
- return
-
- # Update selected proc data if in WORLD view.
- if self.current_page == "WORLD":
- self.__sim.lib.sal_proc_get_proc_data(self.selected_proc, cast(
- self.selected_proc_data, POINTER(c_uint32)
- ))
-
- # Print MAIN simulation data.
- self.__print_line(
- 1, "SALIS[{}]".format(self.__sim.args.file), curses.color_pair(
- self.__pair_header
- ) | curses.A_BOLD
- )
- self.__print_widget(2, self.__main)
-
- # Print data of currently selected page.
- main_lines = len(self.__main) + 3
- self.__print_header(main_lines, self.current_page)
- self.__print_widget(main_lines + 1, self.__pages[self.current_page])
-
- # Print special widgets (WORLD view and PROCESS list).
- if self.current_page == "WORLD":
- self.world.render()
- elif self.current_page == "PROCESS":
- self.__print_proc_list()
-
-
- ###############################
- # Private methods
- ###############################
-
- def __set_colors(self):
- """ Define the color pairs for the data printer.
- """
- curses.start_color()
- curses.use_default_colors()
- self.__pair_header = self.get_color_pair(curses.COLOR_BLUE)
- self.__pair_selected = self.get_color_pair(curses.COLOR_YELLOW)
- self.__pair_error = self.get_color_pair(curses.COLOR_RED)
-
- def __get_screen(self):
- """ Prepare and return the main curses window. We also set a shorter
- delay when responding to a pressed escape key.
- """
- # Set a shorter delay to the ESCAPE key, so that we may use it to exit
- # Salis.
- os.environ.setdefault("ESCDELAY", "25")
-
- # Prepare curses screen.
- screen = curses.initscr()
- curses.noecho()
- curses.cbreak()
- screen.keypad(True)
- curses.curs_set(False)
-
- # We need color support in order to run the printer module.
- if curses.has_colors():
- self.__set_colors()
- else:
- raise RuntimeError("Error: no color support.")
-
- return screen
-
- def __get_inst_list(self):
- """ Parse instruction set from C header file named 'instset.h'. We're
- using the keyword 'SALIS_INST' to identify an instruction definition,
- so be careful not to use this keyword anywhere else on the headers.
- """
- inst_list = []
- inst_file = os.path.join(self.__sim.path, "../include/instset.h")
-
- with open(inst_file, "r") as f:
- lines = f.read().splitlines()
-
- for line in lines:
- if line and line.split()[0] == "SALIS_INST":
- inst_name = line.split()[1][:4]
- inst_symb = line.split()[3]
- inst_list.append((inst_name, inst_symb))
-
- return inst_list
-
- def __get_proc_elements(self):
- """ Parse process structure member variables from C header file named
- 'process.h'. We're using the keyword 'SALIS_PROC_ELEMENT' to identify
- element declarations, so be careful not to use this keyword anywhere
- else on the headers.
- """
- proc_elem_list = []
- proc_elem_file = os.path.join(self.__sim.path, "../include/process.h")
-
- with open(proc_elem_file, "r") as f:
- lines = f.read().splitlines()
-
- for line in lines:
- if line and line.split()[0] == "SALIS_PROC_ELEMENT":
- proc_elem_name = line.split()[2].split(";")[0]
-
- if proc_elem_name == "stack[8]":
- # The stack is a special member variable, an array. We
- # translate it by returning a list of stack identifiers.
- proc_elem_list += ["stack[{}]".format(i) for i in range(8)]
- else:
- # We can assume all other struct elements are single
- # variables.
- proc_elem_list.append(proc_elem_name)
-
- return proc_elem_list
-
- def __get_main(self):
- """ Generate main set of data fields to be printed. We associate, on a
- list object, a label to each Salis function to be called. The following
- elements get printed on all pages.
- """
- return [
- ("e", "cycle", self.__sim.lib.sal_main_get_cycle),
- ("e", "epoch", self.__sim.lib.sal_main_get_epoch),
- ("e", "state", lambda: self.__sim.state),
- ("e", "autosave", lambda: self.__sim.autosave),
- ]
-
- def __get_pages(self):
- """ Generate data fields to be printed on each page. We associate, on a
- list object, a label to each Salis function to be called. Each list
- represents a PAGE. We initialize all pages inside an ordered dictionary
- object.
- """
- # The following comprehensions build up widgets to help up print sets
- # of data elements. The use of nested lambdas is needed to receive
- # updated values.
- # Instruction counter widget:
- inst_widget = [("e", inst[0], (lambda j: (
- lambda: self.__sim.lib.sal_mem_get_inst_count(j)
- ))(i)) for i, inst in enumerate(self.inst_list)]
-
- # Evolver module state widget:
- state_widget = [("e", "state[{}]".format(i), (lambda j: (
- lambda: self.__sim.lib.sal_evo_get_state(j)
- ))(i)) for i in range(4)]
-
- # Selected process state widget:
- selected_widget = [("p", element, (lambda j: (
- lambda: self.selected_proc_data[j]
- ))(i)) for i, element in enumerate(self.proc_elements)]
-
- # With the help of the widgets above, we can declare the PAGES
- # dictionary object.
- return OrderedDict([
- ("MEMORY", [
- ("e", "order", self.__sim.lib.sal_mem_get_order),
- ("e", "size", self.__sim.lib.sal_mem_get_size),
- ("e", "allocated", self.__sim.lib.sal_mem_get_allocated),
- ("s", ""),
- ("h", "INSTRUCTIONS"),
- ] + inst_widget),
- ("EVOLVER", [
- ("e", "last", self.__sim.lib.sal_evo_get_last_changed_address),
- ("e", "calls", self.__sim.lib.sal_evo_get_calls_on_last_cycle),
- ] + state_widget),
- ("PROCESS", [
- ("e", "count", self.__sim.lib.sal_proc_get_count),
- ("e", "capacity", self.__sim.lib.sal_proc_get_capacity),
- ("e", "first", self.__sim.lib.sal_proc_get_first),
- ("e", "last", self.__sim.lib.sal_proc_get_last),
- ("e", "selected", lambda: self.selected_proc),
- ]),
- ("WORLD", [
- ("e", "position", lambda: self.world.pos),
- ("e", "zoom", lambda: self.world.zoom),
- ("e", "selected", lambda: self.selected_proc),
- ("s", ""),
- ("h", "SELECTED PROC"),
- ] + selected_widget),
- ])
-
- def __print_line(self, ypos, line, attrs=curses.A_NORMAL, scroll=True):
- """ Print a single line on screen only when it's visible.
- """
- if scroll:
- ypos -= self.__main_scroll
-
- if 0 <= ypos < self.size[0]:
- # Curses raises an exception each time we print on the screen's
- # edge. We can just catch and ignore it.
- try:
- line = line[:self.size[1] - 1]
- self.screen.addstr(ypos, 1, line, attrs)
- except curses.error:
- pass
-
- def __print_header(self, ypos, line):
- """ Print a bold header.
- """
- header_attr = curses.A_BOLD | curses.color_pair(self.__pair_header)
- self.__print_line(ypos, line, header_attr)
-
- def __print_value(self, ypos, element, value, attr=curses.A_NORMAL):
- """ Print a label:value pair.
- """
- if type(value) == int:
- if value == ((2 ** 32) - 1):
- # In Salis, UINT32_MAX is used to represent NULL. We print NULL
- # as three dashes.
- value = "---"
- elif self.__print_hex:
- value = hex(value)
-
- line = "{:<10} : {:>10}".format(element, value)
- self.__print_line(ypos, line, attr)
-
- def __print_proc_element(self, ypos, element, value):
- """ Print elements of currently selected process. We highlight in
- YELLOW if the selected process is running.
- """
- if self.__sim.lib.sal_proc_is_free(self.selected_proc):
- attr = curses.A_NORMAL
- else:
- attr = curses.color_pair(self.__pair_selected)
-
- self.__print_value(ypos, element, value, attr)
-
- def __print_widget(self, ypos, widget):
- """ Print a widget (data PAGE) on screen.
- """
- for i, element in enumerate(widget):
- if element[0] == "s":
- continue
- elif element[0] == "h":
- self.__print_header(i + ypos, element[1])
- elif element[0] == "e":
- self.__print_value(i + ypos, element[1], element[2]())
- elif element[0] == "p":
- self.__print_proc_element(i + ypos, element[1], element[2]())
-
- def __clear_line(self, ypos):
- """ Clear the specified line.
- """
- if 0 <= ypos < self.size[0]:
- self.screen.move(ypos, 0)
- self.screen.clrtoeol()
-
- def __print_proc_data_list(self):
- """ Print list of process data elements in PROCESS page. We can toggle
- between printing the data elements or the genomes by pressing the 'g'
- key.
- """
- # First, print the table header, by extracting element names from the
- # previously generated proc element list.
- ypos = len(self.__main) + len(self.__pages["PROCESS"]) + 5
- header = " | ".join(["{:<10}".format("pidx")] + [
- "{:>10}".format(element)
- for element in self.proc_elements[self.__proc_element_scroll:]
- ])
- self.__clear_line(ypos)
- self.__print_header(ypos, header)
- ypos += 1
- proc_id = self.proc_list_scroll
-
- # Print all proc IDs and elements in decimal or hexadecimal format,
- # depending on hex-flag being set.
- if self.__print_hex:
- data_format = lambda x: hex(x)
- else:
- data_format = lambda x: x
-
- # Lastly, iterate all lines and print as much process data as it fits.
- # We can scroll the process data table using the 'wasd' keys.
- while ypos < self.size[0]:
- self.__clear_line(ypos)
-
- if proc_id < self.__sim.lib.sal_proc_get_capacity():
- if proc_id == self.selected_proc:
- # Always highlight the selected process.
- attr = curses.color_pair(self.__pair_selected)
- else:
- attr = curses.A_NORMAL
-
- # Retrieve a copy of the selected process state and store it in
- # a list object.
- proc_data = (c_uint32 * len(self.proc_elements))()
- self.__sim.lib.sal_proc_get_proc_data(proc_id, cast(
- proc_data, POINTER(c_uint32))
- )
-
- # Lastly, assemble and print the next table row.
- row = " | ".join(["{:<10}".format(data_format(proc_id))] + [
- "{:>10}".format(data_format(element))
- for element in proc_data[self.__proc_element_scroll:]
- ])
- self.__print_line(ypos, row, attr)
-
- proc_id += 1
- ypos += 1
-
- def __print_proc_gene_block(
- self, ypos, gidx, xpos, mbs, mba, ip, sp, pair
- ):
- """ Print a sub-set of a process genome. Namely, on of its two memory
- blocks.
- """
- while gidx < mbs and xpos < self.size[1]:
- gaddr = mba + gidx
-
- if gaddr == ip:
- attr = curses.color_pair(self.world.pair_sel_ip)
- elif gaddr == sp:
- attr = curses.color_pair(self.world.pair_sel_sp)
- else:
- attr = curses.color_pair(pair)
-
- # Retrieve instruction from memory and transform it to correct
- # symbol.
- inst = self.__sim.lib.sal_mem_get_inst(gaddr)
- symb = self.inst_list[inst][1]
-
- # Curses raises an exception each time we print on the screen's
- # edge. We can just catch and ignore it.
- try:
- self.screen.addstr(ypos, xpos, symb, attr)
- except curses.error:
- pass
-
- gidx += 1
- xpos += 1
-
- return xpos
-
- def __print_proc_gene(self, ypos, proc_id):
- """ Print a single process genome on the genome table. We use the same
- colors to represent memory blocks, IP and SP of each process, as those
- used to represent the selected process on WORLD view.
- """
- # There's nothing to print if process is free.
- if self.__sim.lib.sal_proc_is_free(proc_id):
- return
-
- # Process is alive. Retrieve a copy of the current process state and
- # store it in a list object.
- proc_data = (c_uint32 * len(self.proc_elements))()
- self.__sim.lib.sal_proc_get_proc_data(proc_id, cast(
- proc_data, POINTER(c_uint32))
- )
-
- # Let's extract all data of interest.
- mb1a = proc_data[self.proc_elements.index("mb1a")]
- mb1s = proc_data[self.proc_elements.index("mb1s")]
- mb2a = proc_data[self.proc_elements.index("mb2a")]
- mb2s = proc_data[self.proc_elements.index("mb2s")]
- ip = proc_data[self.proc_elements.index("ip")]
- sp = proc_data[self.proc_elements.index("sp")]
-
- # Always print MAIN memory block (mb1) first (on the left side). That
- # way we can keep most of our attention on the parent.
- xpos = self.__print_proc_gene_block(
- ypos, self.__proc_gene_scroll, 14, mb1s, mb1a, ip, sp,
- self.world.pair_sel_mb1
- )
-
- # Reset gene counter and print child memory block, if it exists.
- if mb1s < self.__proc_gene_scroll:
- gidx = self.__proc_gene_scroll - mb1s
- else:
- gidx = 0
-
- self.__print_proc_gene_block(
- ypos, gidx, xpos, mb2s, mb2a, ip, sp, self.world.pair_sel_mb2
- )
-
- def __print_proc_gene_list(self):
- """ Print list of process genomes in PROCESS page. We can toggle
- between printing the genomes or the data elements by pressing the 'g'
- key.
- """
- # Print all proc IDs and gene scroll in decimal or hexadecimal format,
- # depending on hex-flag being set.
- if self.__print_hex:
- data_format = lambda x: hex(x)
- else:
- data_format = lambda x: x
-
- # First, print the table header. We print the current gene-scroll
- # position for easy reference. Return back to zero scroll with the 'A'
- # key.
- ypos = len(self.__main) + len(self.__pages["PROCESS"]) + 5
- header = "{:<10} | genes {} -->".format(
- "pidx", data_format(self.__proc_gene_scroll)
- )
- self.__clear_line(ypos)
- self.__print_header(ypos, header)
- ypos += 1
- proc_id = self.proc_list_scroll
-
- # Iterate all lines and print as much genetic data as it fits. We can
- # scroll the gene data table using the 'wasd' keys.
- while ypos < self.size[0]:
- self.__clear_line(ypos)
-
- if proc_id < self.__sim.lib.sal_proc_get_capacity():
- if proc_id == self.selected_proc:
- # Always highlight the selected process.
- attr = curses.color_pair(self.__pair_selected)
- else:
- attr = curses.A_NORMAL
-
- # Assemble and print the next table row.
- row = "{:<10} |".format(data_format(proc_id))
- self.__print_line(ypos, row, attr)
- self.__print_proc_gene(ypos, proc_id)
-
- proc_id += 1
- ypos += 1
-
- def __print_proc_list(self):
- """ Print list of process genomes or process data elements in PROCESS
- page. We can toggle between printing the genomes or the data elements
- by pressing the 'g' key.
- """
- if self.__proc_gene_view:
- self.__print_proc_gene_list()
- else:
- self.__print_proc_data_list()
-
- def __proc_select_by_cursor(self):
- """ Select process located on address under cursor, if any exists.
- """
- # First, calculate address under cursor.
- ypos = self.__curs_y
- xpos = self.__curs_x - World.PADDING
- line_size = self.size[1] - World.PADDING
- address = self.world.pos + (
- ((ypos * line_size) + xpos) * self.world.zoom
- )
-
- # Now, iterate all living processes and try to find one that owns the
- # calculated address.
- if self.__sim.lib.sal_mem_is_address_valid(address):
- for proc_id in range(self.__sim.lib.sal_proc_get_capacity()):
- if not self.__sim.lib.sal_proc_is_free(proc_id):
- proc_data = (c_uint32 * len(self.proc_elements))()
- self.__sim.lib.sal_proc_get_proc_data(proc_id, cast(
- proc_data, POINTER(c_uint32))
- )
- mb1a = proc_data[self.proc_elements.index("mb1a")]
- mb1s = proc_data[self.proc_elements.index("mb1s")]
- mb2a = proc_data[self.proc_elements.index("mb2a")]
- mb2s = proc_data[self.proc_elements.index("mb2s")]
-
- if (
- mb1a <= address < (mb1a + mb1s) or
- mb2a <= address < (mb2a + mb2s)
- ):
- self.selected_proc = proc_id
- break
-
- def __get_minimal(self):
- """ Generate set of data fields to be printed on minimal mode.
- """
- return [
- ("cycle", self.__sim.lib.sal_main_get_cycle),
- ("epoch", self.__sim.lib.sal_main_get_epoch),
- ("procs", self.__sim.lib.sal_proc_get_count),
- ]
-
- def __print_minimal(self):
- """ Print minimal mode data fields.
- """
- self.__print_line(1, "Salis --- Minimal mode")
-
- for i, field in enumerate(self.__minimal):
- self.__print_line(i + 2, "{}: {}".format(field[0], field[1]()))