This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Rybec's profile picture

Accessing unknown APIs

Started by Rybec, 04 December 2013 - 10:11 PM
Rybec #1
Posted 04 December 2013 - 11:11 PM
Note: I will be using the terms "screen" and "monitor" non-interchangably here. A monitor is a hardware device, a screen is something you display on a monitor.

Background:
I'm building a program that is designed to have several "screens" it can display on a network of wired monitors. Each monitor can have it's own seperate display and ends up acting kinda like a touchscreen thin client connected to the "mainframe" (the actual computer). The whole thing is being built as modular as possible with the code split up and organized into multiple files

The idea is that each of these screens will be loaded as an API and have common functions like scrn1.draw(), button handlers, etc. This way I can keep them in seperate files and the main program is mostly just a loader and contains the event loop. The first goal is to make it act as an elevator controller, where all I need to do is connect a new monitor to the network and follow on-screen instructions to set up the new stop. I'm trying to keep it open-ended though, so if later I want to be able to stick power level monitors or liquid level monitors or anything else all i have to do is program a new screen to fit the templete I've made for myself and plop it in a folder where the main program will automatically load it up. In fact, thanks to Pastebin, I can just upload it to my account, stick it's ID in the main updater program on that same account, and the system will do the rest the next time it's chunk is reloaded. Plug and play on all possible levels, because I am lazy.


Question:
The issue I'm having (at the moment) is thus:
I'm loading the screen APIs with the code below. Is there a way to access those APIs programmatically?


function loadAPIs(dir) --Loads all files in a directory
	local lst = fs.list(dir)
	for i = 1, #lst do
		print("Attpemting to load API: " .. lst[i])
		os.loadAPI(dir.."/"..lst[i])
	end -- for
end -- function()

This is all well and good when you know ahead of time that you're going to be putting a file called "config" in your lib folder and after calling loadAPIs("lib") you then directly write config.foo() into your code. But how can I call functions from apis that don't exist when I write the main program? os.loadAPI doesn't return anything or I could just stick pointers to the API into a table like "screens = {lst = <pointer that I'm not getting>}".


Worst-case scenario I know I could make an API of APIs where all it is is a list of variables that point to the screen APIs, but that requires manual editing of more files and I am LAZY… The point is to make adding new content as simple as possible. New monitors are plug-and-play, new screens should be too. Especially if I release this once it's in a release worthy state and other people want to write or modify screens for it.

Not only that, but if I do it that way then my "intro screen" has to be it's own separate entity outside the autoload system.


For the record, I do have the groundwork for this laid and tested: http://imgur.com/fa0JYKT (Not visible to the left is a mockup of my planned elevator shaft, with several more screens with working click counts)
Here I'm using placeholder code to simply write text to the monitors. In full-on go mode instead of m.write("New screen!") it will actually call the initialization screen with the monitor and it's associated data table as arguments. Choosing a screen there will change the active screen variable and tell the main program that that monitor needs to be redrawn. Most screens will even include a button to return to the selection screen. As such, at this point it's just a matter of writing the screens themselves and a way to select them. Admittedly there's a lot of work to be done there and I'm sure I'll need to ask more questions later.
Lyqyd #2
Posted 04 December 2013 - 11:26 PM
Check out what os.loadAPI does in bios.lua. You should have no problems creating a version of it for your own purposes that will keep track of the keys that the tables are loaded into.
Rybec #3
Posted 05 December 2013 - 12:27 AM
Failing that, if I wanted to be totally inelegant, it looks like I could also just make calls to _G[name].func() , but now that I'm aware of the existence of loadfile() I think I can come up with a much better solution all around including a better way of storing screen states for each monitor. Thank you, you've been fantastically helpful.
Rybec #4
Posted 06 December 2013 - 12:44 AM
Okay maybe I don't fully understand what's happening here….


I've got an API called "screen" that at the moment contains only the following (but may contain actual functions later):
list = {}
local dir = "multimon/scr"
local lst = fs.list(dir)
for i = 1, #lst do
	print("   Loading screen: " .. lst[i])
	local tEnv = {}
	setmetatable( tEnv, { __index = _G } )
	local fnAPI, err = loadfile( dir.."/"..lst[i] )
	if fnAPI then
		setfenv( fnAPI, tEnv )
		fnAPI()
	else
		printError( err )
	end
	
	local tAPI = {}
	for k,v in pairs( tEnv ) do
		tAPI[k] =  v
	end
	list[lst[i]] = tAPI	
end -- for
The purpose of this is to load all the files inside the scr directory as if they were APIs, but sticks them in a table instead of into the global environment.
It's loading the code contained in my test file inside the scr directory, I can access the functions therein via screen.list["testScreen"].draw(), it's actually drawing to the correct monitor…


But the code inside doesn't seem to have access to any of my global variables or other APIs. I tried running setfenv( tAPI[k] , tEnv) on each function as it gets copied to tAPI, but that didn't seem to help.
Edited on 05 December 2013 - 11:47 PM