====== 编程规范 ======
为了能够配合既有代码使用,合理利用服务器资源,所有程序必须遵守本编程规范。
===== 总旨 =====
开放式电脑的程序需要:
* 基于事件驱动。
* 如果程序能够被终止(即非除了切断电源才能停止程序),则必须能够 “干净” 地终止。
* 没有资源(如文件句柄)泄露。
* 低耦合高内聚、尽量运用库的功能,不要自己实现即有库已实现的功能。
具体来说,规则如下:
* 如无特殊要求,使用 local 变量和函数。
* 非后台程序中,使用 ''event.pull'' 系列函数处理事件。只有后台程序才可以使用 ''event.listen'' 函数。
* 无论是否为后台程序,键鼠事件必须使用 ''event.pull'' 系列函数处理。目的是为了退出程序后键鼠事件不再被处理。
* 必须能够处理 interrupted 事件,非后台程序中,处理的行为是注销所有事件、定时器、文件句柄等,然后退出整个程序;后台程序中,处理的行为是简单地退出。
* 后台程序必须监听一个事件。该事件触发后能注销所有事件、定时器、文件句柄等,从而 “干净” 地退出后台程序。
* 后台程序必须监听一个事件。该事件触发后能让后台程序恢复显示和处理键鼠事件。
===== 示例 =====
建议您在编写 OpenComputer 程序时,直接复制示例中的代码作为开始模板,然后在此基础上实现您的逻辑。
====1.1. 一个简单的前台程序====
local timer = event.timer(1000, local function() -- 定时器
print("timer!");
end);
while true do
local id, _, x, y = event.pullMultiple("touch", "interrupted");
-- 中断事件
if id == "interrupted" then
print("soft interrupt! exiting...");
event.cancel(timer); -- 清理资源
os.exit(0); -- 退出
-- 点击事件
elseif id == "touch" then
print("user touched! x=", arg["x"], "; y=", arg["y"]);
end
end
====1.2. 一个稍微复杂的前台程序====
local timer = event.timer(1000, local function() -- 定时器
print("timer!");
end);
local ifTerminate = false;
while true do
-- 处理事件
event.pullFiltered(local function (ename, ...)
local ename2handling = {
-- 点击事件
["touch"] = local function()
print("user touched! x=", arg["x"], "; y=", arg["y"]);
end,
-- 红石事件
["redstone_changed"] = local function()
print("redstone changed! side=", arg["side"]);
end,
-- 中断事件
["interrupted"] = local function()
print("soft interrupt! closing...");
-- 这儿不能退出!在 filter 函数之中要返回 true 表示事件被处理了,然后在 pullFiltered 调用之外退出。
ifTerminate = true; -- 标记事件处理结束后退出
end
};
if ename2handling[ename] then -- 这个事件是我们想要处理的
ename2handling[ename]();
return true;
else
return false;
end
end);
if ifTerminate then
event.cancel(timer); -- 清理资源
os.exit(0); -- 退出
end
end
====2. 后台程序====
在没有多任务操作系统的情况下编写在后台运行的程序是很麻烦、很原始的。您需要完全手动处理应用程序切入后台、恢复前台的所有逻辑,就如同在 DOS 中一样。在大部分时刻你不需要让多个程序同时在 opencomputers 上执行。因为 opencomputers 的主要目的是为工业控制以及自动化(即通常有一个固定的职责),而不是当作一个个人电脑来使用。
本代码没有经过实际测试,应该无法符合预期运作。本代码的作用仅仅是供您参考和学习操作系统原理,以及给予一个在 OC 中的 bare bones 的多任务的大致实现和思想。
local ifHide = false; -- 程序是否隐藏(后台运行)
local outputBuffer = ""; -- 输出缓冲区
local timer = event.timer(1000, local function() -- 定时器
appPrint("timer!");
end);
local pullInputThread = nil; -- 一个处理输入事件的 “线程” ——只执行一次的 timer
local redstoneEventHandler = event.listen("redstone_changed", local function(addr, side)
appPrint("redstone! addr=", addr, "; side=", side);
end);
-- 监听将程序至于后台 / 前台的键盘事件
local hideShowKeyEventHandler = event.listen("key_up", local function(addr, char)
-- 如果按下小键盘0,唤起应用
if char == keyboard.keys.numpad0 then
show();
event.push("hide_all_programs"); -- 通知其他程序隐藏自己
-- 如果按下小键盘 1,完全终止应用
elseif char == keyboard.keys.numpad1 then
terminate();
end);
-- 如果收到 hide_all_programs 信号,隐藏自己。
local hideAllProgramsEventHandler = event.listen("hide_all_programs", local function()
hide();
end);
-- 将程序至于后台
function hide()
ifHide = true;
event.cancel(pullInputThread); -- 强制终止正在执行的 pullInput 函数
os.exit(0); -- 直接退出
end
-- 恢复程序于前台
function show()
ifHide = false;
dumpOutBuffer(); -- 将程序的输出 dump 到屏幕上
pullInputThread = event.timer(0, pullInput, 1); -- 执行 pullInput “线程”
end
-- 完全终止程序
function terminate()
event.cancel(timer); -- 注销定时器
event.ignore(redstoneEventHandler); -- 注销红石事件回调
event.ignore(hideShowKeyEventHandler); -- 注销将程序至于后台 / 前台的回调
event.ignore(hideAllProgramsEventHandler); -- 注销 hide_all_programs 信号的回调
end
-- 打印
function appPrint(str)
outputBuffer += str + "\n";
if not ifHide then
print(str);
end
end
-- 在屏幕上显示输出buffer
function dumpOutBuffer()
print(outputBuffer);
end
-- 处理键鼠事件
function pullInput()
while true do
if not ifHide then -- 仅仅在程序不在后台时方处理键鼠事件
local id, _, x, y = event.pullMultiple("touch", "interrupted");
-- 中断事件
if id == "interrupted" then
appPrint("soft interrupt! hidding...");
hide();
elseif id == "touch" then
appPrint("user touched! x=", arg["x"], "; y=", arg["y"]);
end
end
end
end
-- main
show();