Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- SmartLock 0.4 --
- -- advances lock system by Totoro; 10/10/2014 --
- local event = require('event')
- local serial = require('serialization')
- local sides = require('sides')
- local term = require('term')
- local fs = require('filesystem')
- local com = require('component')
- local comp = require('computer')
- -- Таблица констант --
- OWNER = 'Totoro'
- --
- OPEN_TIME = 1
- ALARM_TIME = 5
- RED_STRENGTH = 1
- REALTIME = true
- TIMEZONE = 0
- COOLDOWN = 2
- PORT = 27
- --
- ALLOW = "。◕‿◕。"
- WAIT = "(⊙_◎)"
- DENY = "(¬_¬)"
- --
- DEFAULT_USER = 'Noname'
- DEFAULT_DOOR = 'Door X'
- DEFAULT_LEVEL = 1
- --
- GLASSX = 240
- GLASSY = 55
- GLASS_TIME = 2
- -- Таблица цветов --
- forecolor = 0xFFFFFF
- backcolor = 0x000000
- allowcolor = 0x00FF00
- errorcolor = 0xFF0000
- warncolor = 0xCCCC00
- infocolor = 0x398eb5
- --
- function trytofind(name)
- if com.isAvailable(name) then
- return com.getPrimary(name)
- else
- return nil
- end
- end
- local modem = trytofind('modem')
- local glass = trytofind('openperipheral_glassesbridge')
- local mgpu = trytofind('gpu')
- term.clear()
- function errout(message)
- mgpu.setForeground(errorcolor)
- print('[ERROR] '..message)
- mgpu.setForeground(forecolor)
- end
- function warnout(message)
- mgpu.setForeground(warncolor)
- print('[WARNING] '..message)
- mgpu.setForeground(forecolor)
- end
- function infout(message)
- mgpu.setForeground(infocolor)
- print('[INFO] '..message)
- mgpu.setForeground(forecolor)
- end
- function helpout(message)
- mgpu.setForeground(allowcolor)
- print(message)
- mgpu.setForeground(forecolor)
- end
- local agpu = nil
- for a,b in com.list('gpu') do
- if a ~= mgpu.address then agpu = com.proxy(a); break end
- end
- if agpu == nil then
- errout("Не обнаружена вторая видеокарта!")
- return
- end
- if modem ~= nil then infout("Обнаружен модем. Активирован сетевой интерфейс.") end
- if glass ~= nil then infout("Вы можете использовать удаленный интерфейс через очки.")
- else warnout("Очки не обнаружены. Удаленный интерфейс недоступен.") end
- width, height = mgpu.getResolution()
- -- создаем папку для логов
- if not fs.exists("logs") then
- fs.makeDirectory("logs")
- end
- -- ======================================== L I S T M A N A G E R S ======================================== --
- function readList(filename)
- if fs.exists(filename) then
- local file = io.open(filename, "r")
- local data = file:read("*a")
- local list = serial.unserialize(data)
- file:close()
- return list
- else
- errout("Файл "..filename.." не найден, создан пустой список!")
- return {}
- end
- end
- function saveList(list, filename)
- local file = io.open(filename, "w")
- data = serial.serialize(list)
- file:write(data)
- file:close()
- end
- function toList(list, name, data)
- if data == nil then data = true end
- list[name] = data
- return true
- end
- function fromList(list, name)
- if list[name] then
- list[name] = nil
- return true
- end
- return false
- end
- function contains(list, name)
- return list[name] ~= nil
- end
- function printlist(list)
- if next(list) == nil then print("Таблица пуста.")
- else
- c = 1
- for a,b in pairs(list) do
- print(a, serial.serialize(b))
- c = c + 1
- if c > height-3 then
- print("Любая клавиша для продолжения...")
- event.pull('key_down')
- c = 1
- end
- end
- end
- end
- -- ================================================= L O G S ================================================= --
- log = {}
- log_new = 1
- time_offset = TIMEZONE * 60 * 60
- function toLog(message)
- -- записываем в файл
- local name = 'logs/total.log'
- local file = nil
- if fs.exists(name) then
- file = io.open(name, 'a')
- else
- file = io.open(name, 'w')
- end
- if REALTIME then
- lm = string.sub(fs.lastModified(name), 1, -4)
- nm = tonumber(lm) + time_offset
- dt = os.date("*t", nm)
- date = dt.day..'.'..dt.month..'.'..dt.year..'/'..dt.hour..':'..dt.min
- else
- date = os.date()
- end
- message = date..' | '..message
- file:write(message..'\n')
- file:close()
- -- сохраняем в локальный лог
- table.insert(log, message)
- last_date = date
- end
- function printlog(index)
- if index == nil then
- infout(" Лог событий (полностью):")
- else
- infout(" Лог событий (новое):")
- end
- c = 0
- for i=index or 1, #log do
- print(log[i])
- c = c + 1
- if c > height-3 then
- print("Любая клавиша для продолжения...")
- event.pull('key_down')
- c = 1
- end
- end
- if c == 0 then print("Лог пуст.") end
- log_new = #log + 1
- end
- -- =========================================== P R O C E S S I N G =========================================== --
- function change(id, open)
- door = findDoor(id)
- if door == nil then
- errout("Неверное название/ID двери ('"..tostring(id).."').")
- return false
- end
- -- если дверь открывается - показываем смайлик и записываем время
- if open then
- for a,b in pairs(doorlist[door].screen) do
- show(a, ALLOW)
- end
- doorlist[door].time = comp.uptime()
- end
- -- изменяем сигнал редстоуна
- if doorlist[door].inverted then open = not open end
- for a, b in pairs(doorlist[door].red) do
- local red = com.proxy(a)
- if red == nil then
- errout("Неверный адрес контроллера: "..a.." ("..doorlist[door].name..")")
- else
- for i=1, #b do
- if open then
- red.setOutput(sides[b[i]], RED_STRENGTH)
- else
- red.setOutput(sides[b[i]], 0)
- end
- end
- end
- end
- return true
- end
- function changeAll(open)
- for a,b in pairs(doorlist) do
- change(a, open)
- end
- end
- function trytoopen(door, output)
- door = findDoor(door)
- if door == nil then door = last_door end
- if change(door, true) then
- if output then ginfout("Открыто: "..doorlist[door].name) end
- else
- if output then gerrout("Неверное название/ID двери ('"..tostring(door).."').") end
- end
- end
- function findDoor(id)
- if id == nil then return nil end
- if contains(doorlist, tonumber(id)) then return tonumber(id) end
- for a,b in pairs(doorlist) do
- if b.name == id then return a end
- end
- return nil
- end
- function findDoorByAddress(address)
- for a,b in pairs(doorlist) do
- if contains(b.screen, address) then
- return a
- end
- end
- return nil
- end
- function getDoor(id, glasses)
- door = findDoor(id)
- if door == nil then
- gerrout("Неверное название/ID двери ('"..tostring(id).."').")
- return false
- end
- local t = doorlist[door]
- print('ID: '..door, 'Название: '..t.name..' (Уровень: '..t.level..')')
- io.write('Контроллеры: ')
- for a,b in pairs(t.red) do
- io.write(a..' (')
- for i=1, #b do
- io.write(b[i]..' ')
- end
- io.write(') ')
- end
- io.write('\nМониторы: ')
- for a,b in pairs(t.screen) do
- io.write(a..' ')
- end
- if t.inverted then
- print("Инверсный режим редстоуна.")
- end
- print()
- -- вывод на очки
- if glasses then
- glassSplash('ID: '..door, 'Название: '..t.name..' (Уровень: '..t.level..')', 0)
- end
- end
- function setScreens(id, output)
- door = findDoor(id)
- if door == nil then
- errout("Неверное название/ID двери ('"..tostring(id).."').")
- return false
- end
- print("Введите адреса экранов (через пробел): ")
- local data = split(term.read())
- for i=1, #data do
- local screen = com.get(data[i], 'screen')
- if screen == nil then
- errout("Экран "..data[i].." не найден.")
- else
- toList(doorlist[door].screen, screen)
- end
- end
- if output then
- ginfout("Установлены новые мониторы ("..doorlist[door].name..")")
- end
- return true
- end
- function setRedControllers(id, output)
- door = findDoor(id)
- if door == nil then
- errout("Неверное название/ID двери ('"..tostring(id).."').")
- return false
- end
- print("Введите адрес контроллера и стороны (через пробел): ")
- local data = split(term.read())
- local controller = com.get(data[1], 'redstone')
- if controller == nil then
- errout("Неверный адрес контроллера.")
- return false
- end
- if doorlist[door].red[controller] == nil then
- toList(doorlist[door].red, controller, {})
- end
- for i=2, #data do
- if sides[data[i]] == nil then
- errout("Неверная сторона: "..data[i])
- else
- table.insert(doorlist[door].red[controller], data[i])
- end
- end
- if output then
- ginfout("Установлены новые контроллеры ("..doorlist[door].name..")")
- end
- return true
- end
- function setLevel(id, output)
- door = findDoor(id)
- if door == nil then
- errout("Неверное название/ID двери ('"..tostring(id).."').")
- return false
- end
- io.write("Введите уровень двери: ")
- level = tonumber(term.read())
- if level ~= nil then
- doorlist[door].level = level
- if output then
- ginfout(doorlist[door].name..": новый уровень ("..level..")")
- end
- return true
- else
- errout("Неверное значение.")
- end
- return false
- end
- function setInverted(id, output)
- door = findDoor(id)
- if door == nil then
- errout("Неверное название/ID двери.")
- return false
- end
- doorlist[door].inverted = not doorlist[door].inverted
- if output then
- if doorlist[door].inverted then
- ginfout("Установлен инверсный редстоун-режим ("..doorlist[door].name..")")
- else
- ginfout("Установлен обычный редстоун-режим ("..doorlist[door].name..")")
- end
- end
- change(door, false)
- return true
- end
- function addDoor(name)
- if name == nil then
- errout("Неверное название двери.")
- return false
- end
- door = findDoor(name)
- if door then
- warnout("Такая дверь уже есть в базе.")
- return false
- end
- table.insert(doorlist, {name = name, level = DEFAULT_LEVEL, screen = {}, red = {}})
- id = findDoor(name)
- setLevel(id)
- setScreens(id)
- setRedControllers(id)
- change(id, false)
- infout("Новая дверь успешно добавлена.")
- getDoor(id)
- return true
- end
- function addAlarm(data)
- if data[2] == nil then
- errout("Неверное название триггера.")
- return false
- end
- local n = tonumber(data[3])
- if n == nil then
- errout("Неверное условие.")
- return false
- end
- local red = com.get(data[4], 'redstone')
- if red == nil then
- errout("Неверный адрес красного контроллера.")
- return false
- end
- local side = sides[data[5]]
- if red == nil then
- errout("Неверно задана сторона контроллера.")
- return false
- end
- toList(alarmlist, data[2], {condition = n, red = red, side = side})
- infout("Новый триггер успешно создан.")
- end
- function offAlarms(current_time)
- for a,b in pairs(alarmlist) do
- if b.time ~= nil then
- if (current_time-b.time) > ALARM_TIME then
- changeAlarm(a, false)
- end
- end
- end
- end
- function checkAlarms(value)
- for a,b in pairs(alarmlist) do
- if value >= b.condition then
- changeAlarm(a, true)
- end
- end
- end
- function changeAlarm(name, on)
- if alarmlist[name] ~= nil then
- if on then alarmlist[name].time = comp.uptime()
- else alarmlist[name].time = nil end
- local red = com.proxy(alarmlist[name].red)
- if red ~= nil then
- if on then
- red.setOutput(alarmlist[name].side, 15)
- else
- red.setOutput(alarmlist[name].side, 0)
- end
- else
- gerrout("Неверный адрес контроллера тревоги. ("..a..")")
- end
- end
- end
- function help()
- helpout("Краткая справка:")
- helpout("open - открывает дверь")
- helpout("user - задает уровень пользователя")
- helpout("door - создает новую дверь")
- helpout("alarm - создает новый триггер тевоги")
- helpout("set - редактирует параметр двери")
- helpout("list - выводит список пользователей, триггеров или дверей")
- helpout("get - информация об одной двери/пользователе")
- helpout("delete - удаляет дверь, триггер или пользователя")
- helpout("log - показывает лог событий (или очищает его)")
- helpout("checkup - проверяет наличие проблем")
- helpout("exit - безопасный выход из программы")
- helpout("Введите '<команда> ?' чтобы узнать синтаксис.")
- end
- function split(str)
- local result = {}
- for i in string.gmatch(str, "%S+") do
- table.insert(result, i)
- end
- return result
- end
- function process(command, console)
- -- разбивка строки-команды
- local data = split(command)
- -- команда на открытие двери
- if data[1] == 'help' or data[1] == '?' or data[1] == 'man' then
- help()
- elseif data[1] == 'open' then
- if data[2] == '?' then
- ghelpout("Синтаксис: open [name/ID]")
- else
- name = string.sub(command, 6, -2)
- trytoopen(name, true)
- end
- -- добавление нового игрока в таблицу / изменение существующего
- elseif data[1] == 'user' then
- if data[2] == nil or data[2] == '?' or data[3] == nil then
- ghelpout("Синтаксис: user <name> <level>")
- end
- local name = data[2] or DEFAULT_USER
- local level = tonumber(data[3]) or DEFAULT_LEVEL
- toList(userlist, name, level)
- ginfout("Новый уровень доступа "..name..": "..level)
- -- добавление новой двери в список
- elseif data[1] == 'door' then
- if data[2] == nil or data[2] == '?' then
- ghelpout("Синтаксис: door <name>")
- else
- name = string.sub(command, 6, -2)
- addDoor(name)
- end
- -- создание нового триггера тревоги
- elseif data[1] == 'alarm' then
- if data[2] == '?' then
- ghelpout("Синтаксис: alarm <name> <value> <address> <side>")
- else
- addAlarm(data)
- end
- -- установка параметров
- elseif data[1] == 'set' then
- if data[2] == 'level' then
- name = string.sub(command, 11, -2)
- setLevel(name, true)
- elseif data[2] == 'redstone' then
- name = string.sub(command, 14, -2)
- setRedControllers(name, true)
- elseif data[2] == 'screen' then
- name = string.sub(command, 12, -2)
- setScreens(name, true)
- elseif data[2] == 'inverted' then
- name = string.sub(command, 14, -2)
- setInverted(name, true)
- else
- ghelpout("Синтаксис: set <level/screen/redstone/inverted> <id/name>")
- end
- -- вывод на экран/очки списков
- elseif data[1] == 'list' then
- if data[2] == '?' then
- ghelpout("Синтаксис: list [doors/users/alarms]")
- elseif data[2] == 'door' or data[2] == 'doors' then
- if next(doorlist) == nil then ginfout("Таблица пуста.")
- else
- ginfout(" Таблица дверей:")
- local c = 1
- for a,b in pairs(doorlist) do
- getDoor(a)
- c = c+5
- if c > height-3 then
- print("Любая клавиша для продолжения...")
- event.pull('key_down')
- c = 1
- end
- end
- end
- elseif data[2] == 'alarms' or data[2] == 'triggers' then
- ginfout(" Таблица триггеров тревоги:")
- printlist(alarmlist)
- else
- ginfout(" Таблица пользователей:")
- printlist(userlist)
- end
- -- вывод информации об одной двери/юзере
- elseif data[1] == 'get' then
- if data[2] == nil or data[2] == '?' then
- ghelpout("Синтаксис: get <username/doorname/doorID>")
- end
- if contains(userlist, data[2]) then
- ginfout("Пользователь "..data[2]..", уровень доступа "..userlist[data[2]])
- else
- name = string.sub(command, 5, -2)
- getDoor(name, not console)
- end
- -- удаление двери или игрока из списка
- elseif data[1] == 'delete' or data[1] == 'remove' or data[1] == 'rm' then
- if data[2] == nil or data[2] == '?' then
- ghelpout("Синтаксис: rm <name/id>")
- end
- local name = data[2]
- for i=3, #data do name = name..data[i] end
- door = findDoor(name)
- if door == nil then
- if contains(userlist, data[2]) then
- fromList(userlist, data[2])
- ginfout("Игрок "..data[2].." успешно удален из списка.")
- else
- if contains(alarmlist, data[2]) then
- fromList(alarmlist, data[2])
- ginfout("Триггер "..data[2].." успешно удален.")
- else
- gerrout("Неверное имя/адрес.")
- end
- end
- else
- fromList(doorlist, door)
- ginfout("Дверь '"..name.."' успешно удалена.")
- end
- -- вывод лога
- elseif data[1] == 'log' then
- if data[2] == '?' then
- ghelpout("Синтаксис: log [all/clear]")
- elseif data[2] == 'clear' then
- log = {}
- log_new = 1
- ginfout("Лог очищен.")
- elseif data[2] == 'all' then
- printlog()
- else
- printlog(log_new)
- end
- -- проверка правильности данных
- elseif data[1] == 'checkup' then
- if data[2] == '?' then
- ghelpout("Синтаксис: checkup")
- else
- local errors = 0
- for a,b in pairs(doorlist) do
- for k,v in pairs(b.screen) do
- local screen = com.get(k, 'screen')
- if screen == nil then
- gwarnout("Не найден монитор "..screen.." ("..b.name..")")
- errors = errors + 1
- end
- end
- for k,v in pairs(b.red) do
- local red = com.get(k, 'redstone')
- if red == nil then
- gwarnout("Не найден контроллер "..red.." ("..b.name..")")
- errors = errors + 1
- end
- end
- end
- if errors == 0 then
- ginfout("Проблем не обнаружено.")
- else
- gerrout("Найдены проблемы ("..errors..").")
- end
- end
- -- выход из программы
- elseif data[1] == 'exit' or data[1] == 'quit' or data[1] == 'q' then return false
- else gerrout("Команда не знакома.") end
- -- успешное выполнение
- return true
- end
- -- ============================================ E M O T I C O N S ============================================ --
- function show(address, str)
- agpu.bind(address)
- agpu.setResolution(5,1)
- if str == ALLOW then
- agpu.setForeground(allowcolor)
- elseif str == WAIT then
- agpu.setForeground(warncolor)
- else
- agpu.setForeground(errorcolor)
- end
- agpu.set(1, 1, str)
- end
- -- ============================================== G L A S S E S ============================================== --
- glass_uptime = 0
- function glassClear()
- if glass ~= nil then
- glass.clear()
- end
- end
- function glassAddText(x, y, text, color)
- if glass ~= nil then
- obj = {}
- obj[0] = glass.addText(x+1, y+1, text, 0x000000)
- obj[1] = glass.addText(x, y, text, color)
- return obj
- end
- end
- function glassSplash(message, app)
- if glass ~= nil then
- glassClear()
- local width = #message*5.3; title = '[ Message ]'; color = 0xFFFFFF
- x = GLASSX - width/2; y = GLASSY-10
- if app == 0 then
- color = infocolor
- title = '[ INFO ]'
- elseif app == 1 then
- color = warncolor
- title = '[ WARNING ]'
- elseif app == 2 then
- color = errorcolor
- title = '[ ERROR ]'
- else
- color = allowcolor
- title = '[ HELP ]'
- end
- glass.addBox(x, y, width, 20, color, 0.5)
- glassAddText(GLASSX - 17, GLASSY-11, title, 0xFFFFFF)
- glassAddText(x+10, GLASSY, message, 0xFFFFFF)
- glass_uptime = comp.uptime()
- end
- end
- function ginfout(message)
- infout(message)
- glassSplash(message, 0)
- end
- function gwarnout(message)
- warnout(message)
- glassSplash(message, 1)
- end
- function gerrout(message)
- errout(message)
- glassSplash(message, 2)
- end
- function ghelpout(message)
- helpout(message)
- glassSplash(message, 3)
- end
- -- ======================================== N E T I N T E R F A C E ======================================== --
- function sendPackage(message)
- if modem ~= nil then
- modem.broadcast(PORT, message)
- end
- end
- -- ================================================= M A I N ================================================= --
- if glass ~= nil then glass.clear() end
- if modem ~= nil then modem.open(PORT) end
- userlist = readList('userlist.txt')
- doorlist = readList('doorlist.txt')
- alarmlist = readList('alarlist.txt')
- -- добавляем OWNER'а в суперюзеры, если список только что создан
- if next(userlist) == nil then
- toList(userlist, OWNER, 9000)
- end
- -- все двери - в исходное положение
- print("Закрываю все двери...")
- changeAll(false)
- last_date = 'nil'
- last_call = {address = '', time = 0, user = DEFAULT_USER, pause = COOLDOWN}
- history = {}
- infout("Нажмите любую кнопку, чтобы перейти в режим ввода команд.")
- -- Главный цикл - обновление/обработка --
- while true do
- -- ждем событие
- name, add, a, b, c, d = event.pull(1.0)
- local current_time = comp.uptime()
- -- чистим очки
- if glass_uptime ~= 0 then
- if (current_time - glass_uptime) > GLASS_TIME then
- glassClear()
- glass_uptime = 0
- end
- end
- -- закрываем двери
- for a,b in pairs(doorlist) do
- if b.time ~= nil then
- if (current_time - b.time) > OPEN_TIME then
- change(a, false)
- b.time = nil
- end
- end
- end
- -- выключаем тревогу
- offAlarms(current_time)
- -- обработка пользовательских команд с терминала и очков
- if name == 'key_down' then
- term.write("\n>> ")
- command = term.read(history)
- if not process(command, true) then break end
- elseif name == 'chat_command' then
- if not process(add, false) then break end
- -- обработка "звонков в двери"
- elseif name == 'touch' then
- -- защита от спама
- if (d~=OWNER) and (last_call.address == add and
- last_call.user == d and
- (comp.uptime()-last_call.time)<last_call.pause) then
- last_call.pause = last_call.pause + 1
- warnout('Задержка от спама: '..last_call.pause)
- else
- -- обновляем антиспам-таблицу
- last_call.address = add
- last_call.user = d
- last_call.time = comp.uptime()
- last_call.pause = COOLDOWN
- -- проверяем звонок (один адрес может принадлежать нескольким дверям)
- for door, value in pairs(doorlist) do
- if contains(value.screen, add) then
- -- выводим информацию
- print("\n"..d.." у "..value.name.." (ID: "..door..")!")
- -- отчет для лога
- log_message = d..' | '..value.name.." (ID: "..door..") | "
- net_message = {player = d, doorname = value.name, id = door}
- -- проверка по списку
- if not contains(userlist, d) then
- show(add, WAIT)
- last_door = door
- warnout("В списке его нет. Что делать?")
- toLog(log_message..'?')
- glassSplash(log_message..'?', 1)
- net_message.message = '?'
- else
- -- проверяем триггеры тревоги
- checkAlarms(value.level - userlist[d])
- -- выбираем реакцию
- if value.level > userlist[d] then
- show(add, DENY)
- print("В доступе отказано!")
- toLog(log_message..'X')
- glassSplash(log_message..'X', 2)
- net_message.message = 'X'
- else
- change(door, true)
- print("Доступ разрешен.")
- toLog(log_message..'Y')
- glassSplash(log_message..'Y', 0)
- net_message.message = 'Y'
- end
- end
- -- отсылаем отчет по сети
- net_message.date = last_date
- sendPackage(serial.serialize(net_message))
- end
- end
- end
- -- обрабатывает сообщения по сети
- elseif name == 'modem_message' then
- local data = serial.unserialize(d)
- if data.command == 'open' then
- trytoopen(door.id)
- end
- end
- end
- -- завершение (сохраняем списки, чистим экран)
- if modem ~= nil then modem.close(PORT) end
- saveList(userlist, 'userlist.txt')
- saveList(doorlist, 'doorlist.txt')
- saveList(alarmlist, 'alarlist.txt')
- term.clear()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement