Перейти к публикации
  • записей
    86
  • комментариев
    340
  • просмотров
    220 989

О блоге

Gnawing the way out

Записи в этом блоге

Что делать, если вы провалились под пол

Первым делом, сохраняйте панику. Так интереснее. У нас есть группа в ВК, которая, в отличие от параллельных — тупиковых веток эволюции — жива и даёт продукт в виде постов. Очень интересных. Не любите ВК? Я тоже. Имеем мы канал в IRC: зайти можно даже с телнета. Но лучше с помощью какого-то клиента: HexChat, WeeChat, Quassel. Контактные данные для них: Сервер: irc.esper.net Порт: 6697 TLSv1.2 Канал: #cc.ru Впрочем, присутствует и веб-клиент для обитателей двумерного денежно-временного мира. Надо сказать, у нас даже дискорд-клуб где-то был. Однако не только флуждением занимается Хэш cc.ru. Мы завели организации вот тут: На GitHub. На GitLab. В Trello. Под кроватью. А ещё подпольные мудрости тщательно и бережно схороняются на свалку нашего цитатника, так что башорг вам больше не нужен. Не забудьте, по пути куда-нибудь находясь, записаться в читателей этого элитного клуба. Потому что здесь мы будем писать. Что-то. Отлично. Наши цепкие объятия уже ждут-поджидают вас.

Fingercomp

Fingercomp

 

Что такое IRC

Сегодня нашему каналу в IRC исполняется один годик, поэтому пришло время рассказать, что это, зачем это и как к нему подключиться.   Начнём с понятий.
IRC — это протокол обмена мгновенными сообщениями через интернет. Сделанный в далёком 1988 году, и по сей день он всё ещё юзается из-за удобности, простого масштабирования, простоты и доступности буквально отовсюду, где есть подключение к интернету — вплоть до холодильников.
В общении участвуют клиент и сервер. Клиенты подключаются к серверу и общаются.
Для разделения тем существуют каналы — на каждом отдельные сообщения, темы, люди и так далее. Так что на одном сервере могут быть сотни каналов, никак друг с другом не связанных.   Главное, что нужно понять: IRC — это не чат в браузере, как здесь на форуме. Здесь отдельные серверы, отдельный протокол, и поэтому просто так через браузер не подключиться, набрав адрес сервера. Для подключения к IRC нужно воспользоваться специальной программой — клиентом. Здесь я покажу несколько клиентов и расскажу, как их настроить.  
Веб-клиент Iris IRC
Для ситуаций, когда надо по-быстрому зайти на канал, но клиента нет под рукой или лень настраивать. Для полноценного сидения использовать проблематично, так как требуется грузить жирный браузер, и стабильность подключения так себе.
Кроме того, веб-клиенты — поделки очень плохого качества, неконфигурируемые, отсутствуют банальнейшие фичи, например форматирование, или сделаны криво. Тем не менее.  
Возьмём, например, Iris IRC. Ссылка на него (нацеленный на серверы Esper) находится вверху, в панели навигации (). Штука очень минималистичная.

Сверху вводите свой ник, пишете название канала для подключения (по умолчанию стоит наш), если нужно, ставите галочку и вводите пароль и юзернейм (об этом позже). Однако ставить её необязательно. После этого тыкаете на кнопку. Через несколько секунд появится вот такой интерфейс:

Что здесь видим?
Во-первых, кнопка меню . Советую сразу перейти в Menu ‣ Options и поставить галочку напротив "Automatically colour nicknames", чтобы визуально различать людей на канале — по цвету.
Во-вторых, переключалка каналов . Можно тыкать Alt и цифру от 1 до 9, чтобы быстро переключаться между каналами.
Строка топика — небольшого сообщения с темой обсуждения или просто полезными ссылками.

Ниже находится окно чата, в котором будут отображаться ваши сообщения и сообщения других людей, а так же другие оповещения (например, о заходе человека на канал).
Правее — список ников, подключённых к каналу. Знак "@" перед ником означает операторские привилегии — т.е. админ канала, "+" же ничего не даёт (у нас он является неким знаком отличия для людей, которые часто находятся на канале и чего-то мыслят в программировании, но на других каналах может быть не так).
И, наконец, поле внизу для набора сообщений и команд.  
Чтобы отключиться от сервера, просто закройте вкладку.
Чтобы зайти на другой канал, пропишите /j #имя-канала. Например, /j #cc.ru-server1.  
HexChat
Десктопный клиент IRC, конфигурируемый, довольно удобный и пригодный для повседневного общения. Однако он уже требует несколько более сложной настройки.  
Скачав и установив HexChat, после запуска мы увидим вот такое окошко:

Для начала введите 3 варианта ника (они будут пробоваться использовать поочерёдно в случае, если предыдущий ник занят на сервере). Обычно просто ставят "_" в конец. В поле "User name" введите юзернейм — это общий для всех ник (при этом проверка на занятость юзернейма не производится).
В списке ниже найдите "EsperNet". Нажмите на кнопку , а затем поставьте галку , чтобы быстрее находить эту сеть. После этого можно нажать на кнопку .
Произойдёт подключение к серверу. Используйте команду /j #имя-канала, чтобы зайти на нужные каналы. Например, /j #cc.ru. Появится вот такой интерфейс:

Сверху находится меню. Ниже переключалка каналов. Крестик позволяет закрыть вкладку (и выйти с канала).
Ещё ниже строка заголовка, режимы каналов. Справа список ников на канале, слева — сам чат, ниже — поле ввода сообщений и команд. Можно кликнуть правой кнопкой мыши по вкладке канала и отметить "Autojoin", чтобы автоматически заходить на канал после подключения к серверу.
На данный момент HexChat — рекомендуемый нами клиент для Windows и Mac.       KVIrc
Объективно: вроде всё по стандарту, использовать можно. Субъективно: куча ненужных кнопок, прокладок, интерфейсов, всё запутано и намешано, выглядит ужасно. Поэтому настоятельно рекомендую не использовать этот клиент. В любом случае, рассказать о нём стоит.  
После установки и запуска будет предложено выполнить около пяти простых шагов по настройке клиента. Следуйте инструкциям (тем более, что там есть русский язык). Появится вот такое окошко:

Нажмите на иконку , введите в поле под списком "EsperNet". Затем нажмите на , в то же поле введите "irc.esper.net". Нажмите "Connect Now" и затем "OK".
Появится вот такое диалоговое окно:  
Введите в верхнее текстовое поле имя канала (например, "#cc.ru") и нажмите "Join", а потом "Close".
Наконец, можно использовать главное окно:  
Сверху меню, ниже ещё всякие кнопки для действий типа подключения к новому серверу. Ниже топик, режимы канала, потом список ников, сам чат и поле ввода. Ниже статусная строка.       WeeChat
Очень продвинутый, невероятно удобный клиент для Linux. Запускается и работает в терминале, использует ncurses, поэтому даже иксы не требуются. Для Linux однозначно рекомендую, настроив weechat, как нужно, больше другие клиенты использовать не захочется.  
После установки и запуска появляется вот такой непримечательный вид:

Пишем /set irc.server_default.nicks ник,ник_,ник__, чтобы выставить ник. Затем /server add esper irc.esper.net/6697 -ssl.
После этого можно прописать /connect esper для подключения к серверу. И дальше уже /join #имя-канала.

Общаться можно уже и так, а за дополнительными фишками обращаться нужно к мануалу.  
Для телефонов тоже есть свои клиенты, но тут я ничего посоветовать не могу.   Это были клиенты. Но просто поставив их, особо толка не будет. Поэтому сейчас будет несколько штук IRC, общие для всех клиентов.   Помимо каналов на сервере можно напрямую общаться с каким-либо человеком. Для этого нужно прописать /msg <ник> <сообщение> (например, /msg fingercomp привет). В большинстве клиентов можно открыть вкладку (или буфер) для общения с человеком, как для каналов, с помощью команды /query <ник> (например, /query fingercomp).   Есть ещё команда /me. Если использовать её, то вместо <ник> сообщение будет показано что-то вроде * ник сообщение. Так можно отправить сообщения от третьего лица (вроде "fingercomp написал гайд").   Команда /notice — это та же отправка сообщения. Она немного отличается видом в клиентах, но всё равно видна всем на канале или собеседнику, в зависимости от того, кому направить сообщение. Смысл команды — предотвратить вызов ботом команд других ботов.   Чтобы уйти с канала, можно использовать /part <сообщение выхода>. Сообщение будет показано другим людям в оповещении, например так:
Можно вообще от всего сервера отключиться с сообщением, как выше. Используйте команду /quit <сообщение выхода>.   Авторизация на EsperNet.
Зачем нужна авторизация? Прежде всего, чтобы автоматически получать какие-либо права. Например, на канале #cc.ru-server1 (туда транслируется чат с сервера) мы используем это, чтобы автоматически выдавать право отправлять сообщения на сервер.   Чтобы зарегистрироваться, нужно зайти с нужного ника и прописать /msg nickserv register <пароль> <email>, например /msg nickserv register zxcvbnM1 fingercomp@example.com. На ящик придёт сообщение от Esper, в котором будет команда для подтверждения регистрации. Её нужно скопировать и выполнить (то есть написать в строку ввода).
Чтобы затем залогиниться, используйте команду /msg nickserv identify <пароль>.   А теперь последуют вещи, которые есть только на нашем канале #cc.ru.
У нас есть правила, которые желательно соблюдать. Ссылочка на них в топике: https://git.io/vwLXq
Основной бот на канале — brote. У него есть множество команд: от погоды до опросников. Список команд можно получить с помощью команды .help. Брот также обрабатывает команды в ЛС.
В топике после даты я помещаю всякие интересные события, ссылки и прочее. Так что иногда лучше смотреть на топик.
Ведётся статистика всего канала — анализируются логи с середины ноября, хотя канал существовал примерно полгода до этого. Вот ссылка: https://stats.fomalhaut.me/. Можно поизучать — достаточно интересная штука.
Темы обсуждений могут быть абсолютно разными — от размеров очередей в больницах до новых фич в языке Rust. Но в любом случае я постараюсь ответить на все вопросы по программированию на Lua, отправленные на канал. Даже в середине обсуждения — тогда, может, не сразу, но обязательно отвечу.   Кроме того, у нас есть канал #cc.ru-server1. Сюда бот пишет сообщения с чата сервера, сообщения о смерти игроков, а также пишет текущий онлайн в топик. Поэтому для модерирования очень удобная штука.
Чтобы иметь возможность отправлять сообщения из канала на сервер, нужно иметь войс — знак "+", который выдаётся персонально зарегистрированным людям. Я использую несколько критериев для оценки, например активность и адекватность игрока. За любое нарушение правил сервера через IRC следует вечная блокировка возможности отправить сообщения.
Но и без войса можно просто сидеть и читать чат.   В целом, это всё, что я хотел сюда написать. Ждём на наших каналах — подключайтесь, у нас есть печеньки.

Fingercomp

Fingercomp

 

Что изменилось в Computronics с прошлых обзоров

Вкратце проедемся про изменениям с невероятно старой версии 1.5.5 до самой новой, 1.6.3.   1.5.6 / 2015-07-24 Большинство блоков из мода можно покрасить, тыкнув по ним красителем. Интеграция с модом Flamingo (самая нужная фича, конечно): через компы можно заставлять фламинго качаться. Интеграция с Armourer's Workshop. Улучшен улучшенный шифратор. Ключи быстрее генерируются.

1.5.7 / 2015-09-12 Разноцветный апгрейд добавлен, который делает роботов разноцветными. Можно вызвать component.colors.setColor(color: number) и покрасить корпус робота в желаемый цвет. Интересная штука.

1.5.8 / 2015-10-11 Эффект hive_mind для наномашинок. Часть интеграции с Forestry.
Включив, можно тыкнуть пропитанной палкой из Forestry по желаемому улью с пчёлами - пчёлы станут летать над головой.
Палкой же направлять можно пчёл на желаемую цель: пчёлы полетят к ней и станут жалить.
Если отключить эффект, то пчёлы будут жалить игрока.

1.5.9 / 2015-10-22 Чатбоксики креативные стали писать во все измерения.

1.6.0 / 2015-11-28 Аудиокабели добавились. Их можно подключать к кассетным проигрывателям и к динамикам. Динамики будут воспроизводить звук вместо проигрывателя. Убрана поддержка NedoComputers, потому что этот мод сдох.


Спустя год выпускается огромнейший апдейт мода, поэтому дальше расписываю фичи детальнее.   1.6.1 / 2016-11-12 Мод портирован на 1.8.9, 1.9.4 и 1.10. На новых версиях майна используется улучшенный кодек DFPWM1a. Старые записи будут очень тихими, поэтому их нужно переконвертировать с помощью LionRay. С новым кодеком гораздо меньше шума будет. Добавлена шумовая карта. Отключается от пищащей карты тем, что можно для каждого из 8 каналов задать тип волны: синусоида, меандр, треугольная и пилообразная. Допольнительно появляется буфер операций: можно добавить ноты и затем вызвать component.noise.process(), чтобы проиграть. Добавлена звуковая карта. Как работает этот монстр, я описывал в предыдущей записи. Потом опишу функции покруче, а пока можно побикать. Интеграция с TIS-3D, модом от Sangar. Это и новые модули: разноцветный, считыватель дискет, самовыпиливатель - и документация в мануале TIS-3D. Интеграция с OC 1.6.
Добавлены дополнительные штуки для серверов. Плата с лампочками. Ключом можно менять конфигурацию, изменяя положение и количество лампочек. Как компонент предоставляет функции для включения/отключения лампочек и изменения цвета (True color). SSD. Расшифровывается как Server Self-Destructor. Очередная версия самой нужной вещи - самовыпиливателя, но для серверов. Если взорвать через функцию специальную, в серверной стойке пропадут все вещи. Серверный вариант батарейки, которую можно пихнуть в стойку. Можно ещё считывать количество энергии. Плата с переключателями. Можно тыкать по кнопочкам, включая или выключая их. А сервер потом может считывать положение переключателей. По просьбам трудящихся.
[*]Добавлена функция getPosition() в проигрыватель, чтобы узнать текущую позицию на кассете. Именно: её несколько лет не было. Ну что поделать, бывает. [*]Стандартная программка для работы с проигрывателем tape починена для работы с HTTPS. Можно менять размер чанка и таймаут ожидания. И tape wipe добавилась для стирания всей инфы на кассете. [*]1.7.10 Цифровый сигнальный контроллер (интеграция с RailCraft). Надеюсь, когда-нибудь дойдут руки рассказать, как она работает. Но не тут. [*]1.7.10 Цифровый сигнальный приёмник (интеграция с RailCraft). [*]1.7.10 Рецепты для Gregtech 6. [*]1.8+ Больше не требуется устаналивать Asielib: либа встроена в мод.

1.6.2 / 2017-02-25 Фиксы, фиксы и ещё фиксы. Фиксы крашей, фикс тихих звуков в звуковых карточках, дискеты можно теперь получить, наконец, с помощью ключа.

1.6.3 / 2017-04-21 Добавлен синтезатор речи. Так как это охрененная штука, без пинка её так просто не завести. Чтобы она работала, нужно поставить сам синтезатор речи (MaryTTS с поддержкой Forge), файл голоса и файл языка. Женский голос и английский язык, например. К счастью, эти файлы нужны только на сервере. На клиент тянуть их не нужно.
 
Работать так:
speech_box.say(text: string) -- что-то сказать.
speech_box.stop() -- остановить воспроизведение речи.
speech_box.isProcessing():boolean -- проверить, воспроизводится ли сейчас речь.
speech_box.setVolume(volume: number) -- установить громкость (от 0 до 1). Блоки теперь крафтятся не из земли и палок, наконец-то, а из чего-то повеселее. Используются предметы из OpenComputers. Динамики могут смотреть в любую из шести сторон. Директория для кассет перемещена в папку мира. При обновлении нужно перетащить её из папки кубача в папку мира, соответственно. Модуляция из-за глупой ошибки не работала вообще. Ну и ADSR не работал, если время Attack (нарастания) равно было нулю.

Как-то так. За мною остаётся ещё долг в виде продолжения обзора этого интересного модика, но всё как-то лень. Пока можно спрашивать меня, как работают вещи из CX, читать страницы в мануале и документацию к методам. Для большинства вещей этого более чем достаточно.   Have fun :P

Fingercomp

Fingercomp

 

Ставим эмулятор OpenComputers

Наткнулся на интересный эмулятор: https://git.io/vOJaq Написан на Lua. Эмулирует OpenComputers. В этой записи небольшой я расскажу немного о том, как варить пельмени использовать этот эмулятор.

Использовать его так же, как и OpenOS!

Установка:
Linux git clone https://github.com/gamax92/OCEmu.git. Копируем содержимое репозитория. Устанавливаем зависимости: Lua 5.2 SDL2

Далее нас потребуется версионированный luarocks. Т.е., под Lua 5.2 либы в одной директории, под 5.3 — в другой, ну и так далее. На Арче и Маках ничего сложного:
Arch pacman -S lua52 luarocks5.2 lua52-filesystem lua52-sec lua52-socket
Затем переходим к пункту Luarocks.
Mac brew install luabrew install sdl2
Затем переходим к пункту Luarocks.  
Ubuntu
А вот тут уже чуть сложнее. По дефолту луарокс из репы швыряет всё в одну кучу, и потому нужно скомпилировать самому. Ничего особо сложного. Качаем пакет отсюда, распаковываем (tar xvf <имя файла>) и переходим в директорию. Зтем пишем: ./configure --lua-version=5.2 --lua-suffix=5.2 --versioned-rocks-dirmake buildsudo checkinstall
Можно sudo make install, но я лично так не делаю, так как команда выше создаёт пакет сразу, а мейк-инстолл — тупо копирует.
После этого — идём к Luarocks.  
Luarocks
И, наконец, устанавливаем библиотеки через luarocks-5.2: luarocks-5.2 install luafilesystem luarocks-5.2 install luautf8 luarocks-5.2 install luasocket luarocks-5.2 install luasec luarocks-5.2 install --server=http://luarocks.org/dev luaffi[il]

Если вы не страдаете ненавистью к SVN, устанавливаем [il]subversion и пишем make в директории с эмулятором для скачивания OpenOS.
Иначе же открываем https://github.com/MightyPirates/OpenComputers и сами скачиваем src/main/resources/assets/opencomputers/loot в src/loot, src/main/resources/assets/opencomputers/lua в src/lua и src/main/resources/assets/opencomputers/unifont.hex в папку src/ эмулятора.  
Windows
Бинарники: x32 / x64.
Если же хочется самим скомпилировать: Устанавливаем MSYS2, запускаем. Пишем update-core. Закрываем окно и открываем снова. Обновляем остальное командой pacman -Su. Пишем pacman -S mingw-w64-i686-toolchain. Закрываем и в "Пуске" ищем MinGW Win32 Shell или MinGW Win64 Shell, в зависимости от разрядности системы. Открываем именно его. В терминале переходим в папку с эмулятором и пишем ./msys_setup_ocemu.sh. Это сделает всё, что нужно.
 
Запуск: Чаще всего достаточно просто запустить lua5.2 boot.lua в директории src/. Это живенько запустит эмулятор с последней OpenOS. Можно указать путь для эмулируемой машины в аргументе: lua5.2 boot.lua ~/my/machine. BIOS машины хранится в $HOME/.ocemu или %APPDATA%\.ocemu, в зависимости от ОС. Можно изменить его код. Запустите emucfg в OpenOS — это конфигуратор эмулятора. Можно выбрать компоненты: [insert] для его добавления, [Delete] для удаления. Если запустить несколько сессий эмулятора, можно даже "общаться" между ними через эмулированные модемы.


Сетевые соединения между эмуляторами (модемы)i
Крутая штука. Так как ещё ни в одном эмуляторе не было модемов, надо акцентировать внимание на этом обязательно. Создаём две директории для эмулируемых машин (у меня: ~/.ocemu/machines/m1 и ~/.ocemu/machines/m2). В каждую из них копируем файл ~/.ocemu/ocemu.cfg. Открываем первый файл и меняем все адреса в настройке "components". Это необходимо для того чтобы эмулятор считал, что это два разных компьютера. Открываем второй файл и тоже меняем адреса. Достаточно сделать так, что бы они были не такие же, как в первой машине. Можно даже настройку по умолчанию оставить. Запускаем два эмулятора: ./boot.lua ~/.ocemu/machines/m1 & ./boot.lua ~/.ocemu/machines/m2 & (надо запустить их, очевидно, параллельно). И всё, теперь компьютеры могут общаться друг с другом!

 
  OpenOS 1.6
И в бонус записи — предварительный обзор ключевых изменений OpenOS. PIPING. По стандартам. Я не буду объяснять смысл термина, предлагаю просто прописать cat /init.lua | head --lines=25. С grep это тоже работает. Стооооп. Новые программы: head, grep, touch — похожее на Лялеховские программы. Не забывайте про man: man grep. Спасает и помогает. Ещё проги: sleep (спит указанное время), source (считать файл и выполнить каждую строку как команду OpenOS). Тонны изменений в term, pipes, guid, transforms — в библиотеках OpenOS. Терминал теперь только для одного экрана, мультиэкранность хотят сделать отдельной библиотекой. И множество изменений в логике программ, ключах, аргументах.
 
  Огромное спасибо @Krutoy и @Strateg за помощь в установке на Windows.
Если встретились с проблемой при установке на Win, посетите этот топик: http://computercraft.ru/topic/1512-%E2%9C%94-zakaz%E2%84%96-013-vypolneno-binarniki-emuliatora-ocemu-na-windows/

Fingercomp

Fingercomp

 

Разрешение зависимостей в пакетном менеджере с версиями

В прошлой части:
Вы-то прогу скопировать/разархировать и сами можете, вот только если программа зависит от другой, а та — от двух других, и т. д., вам это надоест. Людям надоело. Создали пакетные менеджеры.       Итак, давайте сделаем программу для установки пакетов. Очевидно, что просто так в рандомном порядке пакеты поставить нельзя: нам надо сначала брать пакеты без неразрешённых зависимостей и подниматься вверх.       Итак, у нас есть простая функция, которая составляет список пакетов для последовательной установки без ломаний....
...Но время шло, и появилось такое явление как версии. Вот о них мы сегодня и побеседуем.  
Зачем нужны версии? Нуууу, наверное, чтобы отмечать разные варианты кода.
Зачем ПМ нужны версии? Хм, чтобы быть уверенным, что при установке пакета, зависимости будут именно такие, которые были при написании кода.   Ну, то есть. Вчера у нас была одна функция сделатьХорошо() и попала в релиз 1.0.0, а сегодня она переименована в сделатьПлохо(), сменили код этой функции, да ещё впридачу накинули ничегоНеСделать(). Всё это в релизе 2.0.0. Но если новый код будет использовать ничегоНеСделать(), то его версия 1.0.0 не устроит — там ведь функции нет. А если нужно будет сделатьХорошо(), то в версии 2.0.0 её уже не будет.   Люди — существа чертовски изобретательные, так что форматы версий у нас тоже всяко-разно изобретательны. Начиная от даты релиза (20161023) и номером билда (1243, она ещё ревизией зовётся), заканчивая полноценным семантическим версионированием. Первые потуги нас интересовать будут не сильно, а вот про SemVer можно поговорить.   Спецификация SemVer прямо говорит: версия задаётся тремя числами, разделённые точкой. Первое число — мажорная версия, увеличивается, когда происходят "ломающие" изменения типа удаления старых функций, второе — минорная версия, которая на новых фичах обычно увеличивается, а третье — патчи, это всякие багфиксы.   Вот сферическая версия в вакууме: 1.2.3.   На самом деле, в описании semver задано несколько правил, например, место пререлиза (например, 1.2.3-dev), метаданных (например, 1.2.3+build-15+20161023+amd64), ну и т.д. Если интересно — можете почитать, ссылочка в конце.   Так вот, здесь попробуем организовать разрешение зависимостей с версиями. Не будем брать пока очень мудрёную систему конфликтов.   Начнём с манифеста. Дополним его указанием версий: { "name": "pkg1", "versions": [ { "number": "1.0.0" , "files": [ { "url": "http://example.com/pkg1/1.0.0/file1", "path": "/opt/pkg1/file1" } , { "url": "http://example.com/pkg1/1.0.0file2", "path": "/opt/pkg1/file2" } ] , "depends": [ { "name": "pkg11", "version": "^1" } , { "name": "pkg12", "version": "1.6.2" } ] } ]}
Ну и по аналогии с другими пакетами. Вот такое чудо у нас должно получиться.
 
Напомню, чем у нас закончилась прошлая часть: def resolveDeps(name, resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] if name in unresolved: raise ValueError("circular dependencies detected") if name in resolved: return resolved unresolved.append(name) if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: resolveDeps(dep, resolved, unresolved) resolved.append(name) del unresolved[unresoved.index(name)] return resolved
Давайте перепишем эту функцию так, чтобы она работала после наших изменений в манифесты: def resolveDeps(name, resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] if name in unresolved: raise ValueError("circular dependencies detected") if name in resolved: return resolved unresolved.append(name) if not isInstalled(name): manifest = getManifest(name) version, data = getLatestVersion(manifest) # получаем последнюю версию for dep in latest["deps"]: resolveDeps(dep["name"], resolved, unresolved) resolved.append({"name": name, "version": version) del unresolved[unresoved.index(name)] return resolved
Всё хорошо и замечательно, вот только толку от того, что мы ввели версии, как-то нет совсем.  
А вот далее нам потребуется очень серьёзная либа-парсер семверов. На Python есть semantic_version, которую я ещё портировал на MoonScript — очень доволен. Но это так, будем пока плавать на более высоком уровне абстракции.   Итак, версии. Тот самый граф, ещё раз:
 
Около стрелочек висят какие-то штуки, ^1, например. Эти штуки, которые мы ещё вписываем в манифесты пакетов, ограничивают варианты версий пакетов, которые можно поставить. ^1 говорит, что можно брать любую версию не менее 1.0.0 и не более следующего мажорного релиза (2.0.0). * говорит, что пофигу абсолютно, какая версия встанет. А точное указание версии, как, например, в случае с 1.6.2, не даёт установиться какой-либо другой версии.   Ну а так как они ограничивают, то и называются они ограничениями (или constriants). Пакетный менеджер — скажем так, классическая задача о соблюдении ограничений. Отнюдь не простая.   Раз есть версии, есть ограничения, нужно эти ограничения, значит, включить в функцию разрешателя. Например, дополнительным аргументом. Давайте так и поступим: def resolveDeps(name, vconstraint="*" resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] if name in unresolved: raise ValueError("circular dependencies detected") if name in resolved: return resolved unresolved.append(name) # Создаём объект ограничения из строки vconstraint = createSemVerConstraint(vconstraint) if not isInstalled(name): manifest = getManifest(name) version, data = vconstraint.match(manifest) # получаем версию, соответствующую ограничению for dep in data["deps"]: resolveDeps(dep["name"], dep["version"], resolved, unresolved) # и не забываем передавать версию требуемую resolved.append({"name": name, "version": version) del unresolved[unresoved.index(name)] return resolved
При запуске resolveDeps("pkg1") мы теперь получим [ { "name": "pkg1-1-1" , "version": "1.0.1" }, { "name": "pkg1-1" , "version": "1.2.4" }, { "name": "pkg1-2" , "version": "1.6.2" }, { "name": "pkg1" , "version": "1.2.3" }]
Вот как это на графе будет:
 
Давайте теперь приспособим функцию install к установке: def install(name): depList = resolveDeps(name) for pkg in depList: manifest = getManifest(pkg["name"]) data = getVersion(manifest, pkg["version"]) for file in data["files"]: download(file["url"], file["path"]
В общем-то, это всё. У нас есть вполне рабочая функция установки пакетов с учётом версии по Semantic Versioning. Однако у неё есть некоторые проблемы.  
Ну, во-первых, мы ошибочно считаем, что если пакет установлен, то у него нужная версия. Если у нас уже будет пакет версии 1.12.53, а мы потребуем ^2, то новая версия не поставится. Рискуем попасть на глюки, баги. Кажется, надо просто обновить пакет!..   Но при таком решении невозможно организовать обновление пакетов.
Вообще. Никак. А почему?   У нас зависимости задаются в версиях. Каждая версия может сменить зависимости. При этом каждая новая версия может не соблюдать ограничения, которые дают зависимые от данного пакеты.   А вот вам адская ситуация:

Так вот, чтобы обновить выделенный пакет без нарушения всех зависимостей, потребовалось разрешать конфликты версий. Это достаточно сложный алгоритм, и о нём мы поговорим как-нибудь потом. Тем более, мне только предстоит ввести в свой ПМ резолвер конфликтов.  
А пока можете почитать спецификацию SemVer.   .

Fingercomp

Fingercomp

 

Разрешение зависимостей в пакетном менеджере

Раз уж я тут пишу понемногу свой крутой пакетный манагёр, расскажу о пакетных менеджерах немного.   Пакетный менеджер — штука сложная. Потому что, хотя задача у него, в общем-то, одна — менеджировать пакеты — сюда включается и установка, и удаление, и обновление, и, вообще, много всякого. Но а так как пока сам не напишешь, ПМ не поймёшь, здесь расскажу об установке пакетов и зависимостей с кодом.   Ещё немного предисловий, о зависимостях. Это ключевая фича ПМ: вы-то прогу скопировать/разархировать и сами можете, вот только если программа зависит от другой, а та — от двух других, и т. д., вам это надоест. Людям надоело. Создали пакетные менеджеры. Теперь программы пакуются в пакеты — а рядом со скомпилированными бинарниками лежит ещё кусок информации: имя пакета, версии, зависимости, авторы, изменения и много-много всяких других полей. При установке данные считываются и далее уже делается, что сказано. Зависимости ли ставятся, ещё ли что-нибудь.   А затем пакетов становится много, появляются репозитории полноценные, ну и так далее.   Итак, давайте сделаем программу для установки пакетов. Ну, почти. Именно полезной нагрузки как таковой не будет, будем использовать такую структуру информации о пакете (назовём это манифестом пакета): { "name": "имя пакета", "files": [ { "url": "http://example.com/bin-1", "path": "/usr/bin/program1" } , { "url": "http://example.com/library-1.so", "path": "/usr/lib/library1.so" } ]}
Пока без зависимостей, всё просто.  
Вот такой код получим: def install(name): # получаем манифест пакета с данным именем manifest = getManifest(name) # проходимся по файлам... for file in manifest["files"]: # ...скачиваем и ставим их в нужные места download(url=file["url"], path=file["path"])
Ничего примечательного, на самом деле. Получаем манифест, скачиваем файлы и пишем "тадаам".  
Давайте сделаем вот такие манифесты: { "name": "pkg1" # имя пакета, "deps": # зависимости [ { "name": "pkg1-1" } , { "name": "pkg1-2" } ], "files": [ { "url": "http://example.com/pkg1/file", "path": "/opt/pkg1/file" } ]} { "name": "pkg1-1", "deps": [ { "name": "pkg1-1-1" } ], "files": [ { "url": "http://example.com/pkg1-1/file1", "path": "/opt/pkg1-1/file1" } , { "url": "http://example.com/pkg1-1/file2", "path": "/opt/pkg1-1/file2" } ]} { "name": "pkg1-1-1", "deps": [], "files": [ { "url": "http://example.com/pkg1-1-1/file", "path": "/opt/pkg1-1-1/file" } ] { "name": "pkg1-2", "deps": [], "files": [ { "url": "http://example.com/pkg1-2/file1", "path": "/opt/pkg1-2/file1" } , { "url": "http://example.com/pkg1-2/file2", "path": "/opt/pkg1-2/file2" } ]}
У нас есть 4 пакета: pkg1, pkg1-1, pkg1-1-1, pkg1-2. Вот граф зависимостей:                    
Очевидно, что просто так теперь тут в рандомном порядке пакеты поставить нельзя. Так как при установке пакета, например, pkg1-1, он совершенно справедливо считает, что его зависимость, pkg1-1-1, уже установлена.   То есть, по-хорошему, нам надо сначала брать пакеты без неразрешённых зависимостей, и подниматься вверх. Однако, есть идея покруче.   Я сейчас наваяю рекурсивную функцию resolveDeps, которая будет, как ни странно, разрешать зависимости: def resolveDeps(name): result = [] # результатирующая последовательность установки пакетов manifest = getManifest(name) for dep in manifest["deps"]: # В Python справедливен код типа `[1, 2, 3] + [4, 5, 6] == [1, 2, 3, 4, 5, 6]`, т.е. склеиваение списков. result = resolveDeps(dep["name"]) + result return result
Если мы дадим ей манифесты, она выдаст вот такой список: ["pkg1-1-1", "pkg1-1", "pkg1-2", "pkg1"] — от менее сложного к более сложному. То, что нужно. Затем мы ставим просто их: for pkg in resolveDeps("pkg1"): install(pkg)
Давайте улучшим алгоритм. Сделаем проверку на установленность: нам ведь не надо повторно скачивать файлы, которые уже есть. def resolveDeps(name): result = [] # Проверяем, установлен ли пакет if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: result = resolveDeps(dep["name"]) + result return result
Если у нас уже поставлен pkg1-1, то получим всего лишь ["pkg1-2", "pkg1"]. Круто!  
Возьмём другой граф:      

Как видно, от pkg1-1-1 зависят сразу 2 пакета: pkg1-1 и pkg1-2. Проблема в том, что на выходе у нас будет ["pkg1-1-1", "pkg1-1-1", "pkg1-1", "pkg1-2", "pkg1"] — ни разу не то, что мы хотели. Давайте это исправим: def resolveDeps(name, resolved=None): resolved = resolved or [] # список уже разрешённых пакетов if name in resolved: # Пакет уже был разрешён, ничего больше не требуется return resolved if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: resolveDeps(dep["name"], resolved) # Теперь список один на всю рекурсию resolved.append(name) # Без рекурсии сюда попасть можно, если пакет не имеет неустановленных зависимостей return resolved
Теперь выхлоп у нас ["pkg1-1-1", "pkg1-1", "pkg1-2", "pkg1"] — как и предписывали.  
А вот вам ещё граф:      
 
Какая тут засада? А у нас дерево циркулярное — не руки циркуляркой отрезает, а в бесконечную рекурсию вводит. Вот как можно этого избежать: def resolveDeps(name, resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] # список ещё не разрешённых пакетов if name in unresolved: # Мы попали сюда через рекурсию. Когда-то пакет уже был добавлен в список unresolved, # после чего функция ушла в рекурсивное разрешение зависимостей этого пакета. # Какой-то из зависимостей в итоге опять имеет данный пакет как зависимость. # Это ошибка, такого быть не должно, паникуем. raise ValueError("circular dependencies detected") if name in resolved: # Пакет уже был разрешён, ничего больше не требуется return resolved unresolved.append(name) if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: resolveDeps(dep["name"], resolved, unresolved) # даём unresolved resolved.append(name) # Не забываем убирать из списка del unresolved[unresoved.index(name)] return resolved
Теперь у нас в данном графе будет сгенерировано исключение, а потому рекурсии бесконечной не произойдёт.  
Итак, у нас есть простая функция, которая составляет список пакетов для последовательной установки без ломаний. Это уже круто, но время шло, и появилось такое явление как версии. Впрочем, об этом поговорим в другой раз. Там есть свои заморочки, с которыми нужно разобраться.   Вот похожая статья, но на английском. Рекомендую ознакомиться.   Лицензия: CreativeCommons Attribution-NonCommercial 4.0 International License

Fingercomp

Fingercomp

 

Прокачка стектрейса

Эта беспрецедентно короткая запись имеет начало своих ног в запросе @Laine_prikol, как-то спросивший в нашей ирке, можно ли стэктрейс сделать не таким тупым. Меня это заинтересовало, и спустя часик выросла очень короткая программка, которая рисует вот такие стэктрейсы: # 0: C field function yield(...) (defined in [C]) # 1: Lua local function f(f=function: 0x55bea5785ef0, a=42, b=24, ...):79 (defined in trace.lua at L78) # 2: Lua local function outer(f=function: 0x55bea608c900, g=function: 0x55bea5785ef0, a=42, b=24):75 (defined in trace.lua at L73) # 3: Lua function <anon>:83 (defined in trace.lua at L72) Заметили что-то необычное? Наконец-то пишется, какие аргументы имеются у функции, потому что это куда информативнее беглому взгляду, чем описание расположения и строки.   Код лежит на гисте: https://gist.github.com/Fingercomp/a688d221356cb371d940b947d0ca90a8. Использованы функции debug.getinfo и debug.getlocal. Аргументы должны писаться даже внутри OC, но уже без значений.

Fingercomp

Fingercomp

 

Перевод постов про звуковую карту (Guide to the Sound Card, English version)

tl;dr: https://gist.github.com/Fingercomp/0773bb0714296c0cb00d70a696d39bb3   Понятия не имею, зачем я сюда об этом пишу, ведь если вы можете этот текст понять, и в оригинале можно прочитать.   В любом случае, получил намёк, что три моих статейки про звуковую карту удовлетворительны в какой-то степени, но они на русском. Видите ли, ситуация с документацией спустя полгода после первого поста не улучшилась никак, так что единственный туториал для неё недоступен для понимания тех, кто не говорит по-русски.   Поэтому я потратил выходные на перевод постов на английский язык. Заняло это отчего-то дольше, чем я ожидал. Результат на гисте.   Все три поста в одном месте. Я там подбросил ещё чутка инфы и терминов и подправил фактические неточности. Мне лень было несколько раз перечитывать один и тот же текст, так что где-то могут остаться очепятки и всякие извороты языковые не к месту.
Но если понимаете английский, то всё равно должно быть удобнее, чем бегать по трём статьям здесь, в блоге. Ну а мне редактировать проще.   В общем, ссылку я оставил, больше мне сказать нечего.

Fingercomp

Fingercomp

 

Парсер CSV

CSV идёт от Comma-Separated Values, что, в общем, довольно точно описывает этот формат хранения таблиц. Вот типичная таблица: aaa,bbb,ccc,dddeee,fff,ggg,hhh
Как видно, строки отделяются \n, а ячейки ­— запятой. Последняя строка может иметь или не иметь \n.   Формат очень простой. Описывается он в RFC 4180. Там всего 7 пунктов. Ну а раз простой, давайте соорудим парсер.   Вот у нас есть строка aaa,bbb,ccc,ddd\neee,fff,ggg,hhh. Задача: сделать из неё [ [ "aaa", "bbb", "ccc", "ddd" ], [ "eee", "fff", "ggg", "hhh" ]]   Так как позже я немного усложню парсер, очевидный вариант со split, которая делит строку, опустим. Сделаем так: def parse_csv(s): # Сюда идёт результат result = [] # Текущая строка row = [] # Текущая ячейка cell = "" # Проходимся по строке for i in range(len(s)): # Текущий символ c = s[i] if c == ",": # Если символ — запятая, закрываем ячейку row.append(cell) cell = "" elif c == "\n": # Если это перевод строки, то закрываем ячейку и строку row.append(cell) cell = "" result.append(row) row = [] else: # Любой другой символ добавляем в ячейку cell += c # Возвращаем результат return result
Запускаем: >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh\n")[['aaa', 'bbb', 'ccc', 'ddd'], ['eee', 'fff', 'ggg', 'hhh']] >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh")[['aaa', 'bbb', 'ccc', 'ddd']]   Действительно, в конце может и не быть \n. Давайте поправим: def parse_csv(s): result = [] row = [] cell = "" for i in range(len(s)): c = s[i] if c == ",": row.append(cell) cell = "" elif c == "\n": row.append(cell) cell = "" result.append(row) row = [] else: cell += c # Если ячейка не пуста if cell: # Закрываем ячейку и строку row.append(cell) result.append(row) return result
Проверяем: >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh\n")[['aaa', 'bbb', 'ccc', 'ddd'], ['eee', 'fff', 'ggg', 'hhh']] >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh")[['aaa', 'bbb', 'ccc', 'ddd'], ['eee', 'fff', 'ggg', 'hhh']]
Замечательно.   Почему я проверяю только ячейку, а не строку ещё? Просто пустая ячейка и непустая строка может быть только тогда, когда на конце строки висит запятая. aaa,bbb,. А это явно запрещено по RFC.  
В текущем виде в ячейке у нас не получится хранить \n и ,. Если первый символ ещё кое-как, то без запятой как-то совсем не весело, верно?
На наше счастье, в спецификации есть и это. Ячейку можно поместить в двойные кавычки (", кто не понял), тогда до следующей кавычки обрабатываться \n и , не будут.   Давайте улучшим наш парсер, добавив поддержку этих самых кавычек. Так как у нас посимвольный парсинг, сделать это гораздо проще. Вот так: def parse_csv(s): result = [] row = [] cell = "" # Начиналась ли текущая ячейка с кавычки quoted = False for i in range(len(s)): c = s[i] if quoted: if c == '"': # Закрывающая кавычка quoted = False else: cell += c else: if c == '"': if not cell: # Открывающая кавычка в начале ячейки quoted = True else: # Кавычка в середине строки: запрещено return False elif c == ",": row.append(cell) cell = "" elif c == "\n": row.append(cell) cell = "" result.append(row) row = [] else: cell += c if cell: if quoted: # Где-то не закрыли кавычки return False row.append(cell) result.append(row) return result   Проверяем:
    Всё верно, кроме последнего. В середине строки в закавыченных строках эти самые кавычки должны быть экранированы вот так: "". Например: "aaa""bbb,ccc",ddd,eee. Давайте починим и это.
def parse_csv(s): result = [] row = [] cell = "" quoted = False # Является ли предыдущий символ кавычкой prevQuote = False for i in range(len(s)): c = s[i] if quoted: if c == '"': # Помечаем, что у нас есть кавычка в середине строки. # Она может быть экранированной. prevQuote = True quoted = False else: cell += c else: if c == '"': if not cell: quoted = True else: if prevQuote: # Если у нас прошлый символ был кавычкой, # то получаем экранированную кавычку. cell += '"' quoted = True prevQuote = False else: return False elif c == ",": row.append(cell) cell = "" # Кавычка была закрывающей prevQuote = False elif c == "\n": row.append(cell) cell = "" result.append(row) row = [] # Кавычка была закрывающей prevQuote = False else: if prevQuote: # Мы ждали кавычку или закрытие ячейки. return False cell += c if cell: if quoted: return False row.append(cell) result.append(row) return result   Опять тестируем:
    Вот и всё. 44 строки кода на Python — и мы можем парсить CSV.
Я также переписал парсер на Lua, опубликовал его в OPPM под libcsv. Можете качать и радоваться. Вот сырцы.   Ну и надеюсь, это было менее сложно, чем мои записи про пакетные менеджеры до этого, и вы смогли прочитать это .

Fingercomp

Fingercomp

 

Пара трюков OpenComputers

Здесь опишу такие штучки, которые могут потребоваться продвинутым OC-программистам (да и просто Луа-программистам).   Busy Idle
С помощью этого трюка можно делать довольно точные задержки, причём с длительностью менее тика. local lastSleep = os.clock()local function sleep(t) local begin = os.clock() while os.clock() - begin < t do if lastSleep - os.clock() >= 3.5 then -- В конфигурации дефолтное значение = 5 секунд, ставим на 1.5 меньше для безопасности. os.sleep(0.05) -- Вынужденная задержка. lastSleep = os.clock() t = t - 0.05 end endend
Проверка по значению
Очень часто в моих программах нужно найти ключ, значение которого соответствует данному. Для этого я написал простую функцию: local function isin(tbl, value) for k, v in pairs(tbl) do if v == value then return true, k end end return falseend
На огромных массивах может и затормозить — скорость работы прямо зависит от длины массива.  
Табличная магия
Рассмотрим этот на первый взгляд обычный пример кода: local tbl1 = {"My", "super", "table", 42}local tbl2 = tbl1tbl2[2] = "cool"for _, tbl in pairs({tbl1, tbl2}) do -- Напечатать значения таблиц for k, v in pairs(tbl) do print(k, v) endend
Многие уверены, что вывод будет такой: 1 My2 super3 table4 421 My2 cool3 table4 42
Но вместо этого получаем: 1 My2 cool3 table4 421 My2 cool3 table4 42
Как видно, изменив значение в одной таблице, изменилась и другая.
Дело в том, что переменная хранит указатель на таблицу, а не саму таблицу. Соответственно, и tbl1, и tbl2 ссылаются на один и тот же массив.
На первый взгляд это кажется ненормальным. Как скопировать-то таблицу? local function copy(tbl) if type(tbl) ~= "table" then return tbl end local result = {} for k, v in pairs(tbl) do result[k] = copy(v) end return resultend
Но из этого можно извлечь очень полезное применение. Когда мы передаём таблицу в аргументы функции, массив не копируется, а даётся указатель на тот же самый. Поэтому можно сообразить такой код: local function removeOddNums(tbl) for k, v in pairs(tbl) do if tonumber(v) and v % 2 == 1 then tbl[k] = nil end endend local table = {5, 26, 249586, 457139, 876, 42, 153}removeOddNums(tbl)
И он будет работать. Этим и объясняется, почему table.sort не возвращает таблицу. У меня не самое полезное применение, однако с помощью таблицы можно создавать "поинтеры", например, так: local numPtr = {42}, а в функциях использовать так: local value = numPtr[1]; numPtr[1] = 666. И уже использовать их в своих вычислениях.  
Думаю, вы найдёте применение этим фокусам. Не самые очевидные моменты, однако иногда требуется.
The end.

Fingercomp

Fingercomp

 

Палитры OpenComputers

Немногие знают, как работают палитры в OpenComputers. Расскажу здесь, как избавиться от необходимости прописывать гектары цветов в палитре, покажу, как упаковываются цвета в OpenComputers и дам пару алгоритмов для работы с индексами.   Сразу условимся, что индексы палитр у нас будут начинаться с нуля.   На каждой из трёх уровней видеокарт и мониторов своя поддерживаемая палитра цветов. Будем двигаться снизу вверх.   Первый уровень
Палитра состоит из двух цветов: чёрного и заданного в конфиге (по умолчанию белого). При конвертации цвета в индекс палитры вернётся ближайший к данному цвет из палитры.   Для определения разницы между цветами здесь и далее используется функция delta. Вот как она выглядит (вместе с функций extract, выделяющей из числа вида 0xABCDEF числа 0xAB, 0xCD, 0xEF): local function extract(color) color = color % 0x1000000 local r = math.floor(color / 0x10000) local g = math.floor((color - r * 0x10000) / 0x100) local b = color - r * 0x10000 - g * 0x100 return r, g, bendlocal function delta(color1, color2) local r1, g1, b1 = extract(color1) local r2, g2, b2 = extract(color2) local dr = r1 - r2 local dg = g1 - g2 local db = b1 - b2 return (0.2126 * dr^2 + 0.7152 * dg^2 + 0.0722 * db^2)end
Теперь можно конвертировать цвет в индекс палитры. Суть такова: выбираем из двух цветов ближайший и возвращаем его. local palette = { 0x000000, CONFIG.monochromeColor}local function t1deflate(color) -- Сначала проверяем, совпадает ли данный цвет -- с каким-либо из палитры for idx, v in pairs(palette) do if v == color then return idx end end -- Составляем таблицу разниц между цветами local deltas = {} for idx, v in pairs(palette) do table.append(deltas, {idx, delta(v, color)}) end -- Сортируем по увеличению разницы table.sort(deltas, function(a, b) return a[2] < b[2] end) -- Первый элемент будет с наимешьней разницей, -- то есть искомый. Возвращаем индекс. return deltas[1][1] - 1end
В случае с конвертацией из из индекса в цвет всё просто. local function t1inflate(index) return palette[index + 1]end
Как и говорил.  
Второй уровень
В палитре второго уровня имеется 16 закреплённых цветов: local palette = {0xFFFFFF, 0xFFCC33, 0xCC66CC, 0x6699FF, 0xFFFF33, 0x33CC33, 0xFF6699, 0x333333, 0xCCCCCC, 0x336699, 0x9933CC, 0x333399, 0x663300, 0x336600, 0xFF3333, 0x000000}
В остальном конвертация из иднекса в цвет и из цвета в иднекс аналогична алгоритмам для первого уровня. Так как они были даны в общем виде, код для них тот же самый. local t2deflate = t1deflatelocal t2inflate = t1inflate
Третий уровень
Палитра третьего уровня содержит уже 256 цветов: первые 16 цветов изменяются, а остальные соответствуют цветам палитры RGB-6-8-5. Это означает, что можно смешивать 6 оттенков красного, 8 оттенков зелёного и 5 оттенков синего. В общем-то, довольно очевидна причина такого выбора: человеческий глаз лучше всего различает оттенки зелёного и хуже всего - оттенки синего.  
В любом случае, здесь алгоритмец будет посложнее. Сначала нужно сгенерировать палитру.
Начнём с первых 16 цветов. Они не включаются в палитру RGB-6-8-5, поэтому их заполнять нужно отдельно. В OpenComputers по умолчанию они содержат оттенки серого. Так как чёрный и белый уже включены в основную, зафиксированную палитру, то заново их дублировать смысла нет. local palette = {}-- grayscalefor i = 1, 16, 1 do palette[i] = 0xFF * i / (16 + 1) * 0x10101end
Таким образом в таблице получаются следующие оттенки серого: 0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78,0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0
Эти цвета мы записываем в индексы от 0 до 15. Теперь нужно сгенерировать остальные цвета - они не изменяются. Здесь будет посложнее.
Посмотрим на картинку с палитрой:

В OpenComputers левая верхняя ячейка палитры (0x000000) имеет индекс 16, а правая нижняя (0xFFFFFF) имеет индекс 255. Индексы распределяются слева направо, сверху вниз. То есть правая верхняя ячейка (0x00FFFF) имеет индекс 55, а вторая сверху и левая (0x330000) - это номер 56. Отсюда вытекает следующий алгоритм нахождения цвета: сначала найти индексы отдельно по R, G, B, затем для каждого из этих трёх индексов найти соответствующий ему оттенок цвета, а затем всё сложить. for idx = 16, 255, 1 do local i = idx - 16 local iB = i % 5 local iG = (i / 5) % 8 local iR = (i / 5 / 8) % 6 local r = math.floor(iR * 0xFF / (6 - 1) + 0.5) local g = math.floor(iG * 0xFF / (8 - 1) + 0.5) local b = math.floor(iB * 0xFF / (5 - 1) + 0.5) palette[idx + 1] = r * 0x10000 + g * 0x100 + bend
К слову сказать, math.floor(x + 0.5) - это округление до ближайшего целого.  
Всё. Палитра есть, теперь можно, наконец-то, конвертировать индексы между цветами.
Из индексов получить цвет довольно просто. Достаточно использовать ту же функцию, что и для предыдущих уровней: t3inflate = t2inflate
С обратной же конвертацией всё в разы сложнее. Функция, используемая в OC, подбирает ближайший цвет очень хитрым алгоритмом, который я привожу ниже. local function t3deflate(color) local paletteIndex = t2deflate(color) -- Если цвет из палитры, то используем значение выше for k, v in pairs(palette) do if v == color then return paletteIndex end end -- Иначе используем хитромудрый код local r, g, b = extract(color) local idxR = math.floor(r * (6 - 1) / 0xFF + 0.5) local idxG = math.floor(g * (8 - 1) / 0xFF + 0.5) local idxB = math.floor(b * (5 - 1) / 0xFF + 0.5) local deflated = 16 + idxR * 8 * 5 + idxG * 5 + idxB if (delta(t3inflate(deflated % 0x100), color) < delta(t3inflate(paletteIndex & 0x100), color)) then return deflated else return paletteIndex endend
В общем-то, это всё. Показал портированный со Scala на Lua код, который используется в OpenComputers. С помощью этого можно оптимизировать операции с экраном, выбирая поддерживаемые монитором цвета. И заодно избавиться от таблиц цветов, которые некоторые буквально берут и копипастят в файл, даже не задумываясь об изменяемых цветах палитры.
Особенно это важно, когда берётся значение цвета через gpu.get, потому что следующий код всегда вернёт false: local gpu = require("component").gpugpu.setForeground(0x20AFFF)gpu.setBackground(0x20AFFF)gpu.set(1, 1, "HI")return select(2, gpu.get(1, 1)) == 0x20AFFF
И всё потому, что gpu.get возвращает уже приведённый к индексу из палитры цвет. А 0x20AFFF в палитре, если не менять первые 16 цветов, не имеется.  
Enjoy :P

Fingercomp

Fingercomp

 

Обновление OpenComputers до версии 1.7.3

Эм, здрассьте. Предлагаю поглядеть на новое обновление мода. Очень толстого обновления. Отрегулировали частоту выполнения хука, который шлёт этот ненавистный "too long without yielding", так что теперь и скорость исполнения кода должна гораздо возрасти, и с ошибкой этой код падать реже. Мы проверяли: некая гуи-либа с 1.6 fps до 2.5 fps только благодаря этому работать стала. Оптимизировали производительность ещё и записи на диск. Пошустрее будет — обещают, что в 5–500 раз. Сетевой разделитель (сплиттер) стал компонентом. Можно программно теперь отключать куски сети. Жёсткие диски стало возможным делать Read-Only. Компьютеры CC могут читать сигналы бандлед-редстоуна OC. И наоборот. Функции [il]debug.getlocal[/il] и [il]debug.getupvalue[/il]: возвращают они лишь только имя переменной, но не значение её. И мне кажется, что это уже давно было завезено. Геолайзеры получили методы [il]isSunVisible[/il], [il]canSeeSky[/il] и [il]detect[/il]. Неплохо. В [il]computer.beep[/il] можно писать морзянку. [il]computer.beep("---.---")[/il]. [il]redstone.setOutput[/il] научился ставить значения больше 15. Клавиатуру можно цеплять к монитору, если ещё поставить к непередней стороне блока. Наконец-то. [1.12] Вернули поддержку Project Red. Через адаптер можно теперь работать с камерой реактора IC2. У серверных дисководов тоже есть теперь гуишка (пкм в мире или внутри интерфейса стойки). Торговый апгрейд обзавёлся методом [il]getMerchantId[/il]. Полезно, если жителей куча. [1.12] Вернули поддержку энергии AE2. В конце-то концов: дебаг-карте добавили [il]scanContentsAt[/il]. Больше инфы возвращается для предметов из Draconic Evolution. Вейпоинты стало можно ставить вверх или вниз. Это действительно было слишком контринтуитивным. Связанные карты можно скрафчивать вместе (повяжет на новый канал их). Плюс получать адрес канала при скане стэка. Можно теперь менять цветовой код сундуков Ender Storage. Связанные карты также научились будить компьютер по сигналу, как модемы. Белый и чёрный списки измерений для чанклоадера. Метод [il]disk_drive.media[/il], которым можно получить адрес дискеты внутри дисковода. Поддержка Forge Energy для зарядки предметов вроде батареек и планшетов. Анализатор показывать будет по клику на адаптер ещё и содержащийся в нём компонент. Событие [il]redstone_changed[/il] показывает, какой цвет поменялся на бандлед-кабеле. По шифт-клику компоненты закидываются в соответствии с их уровнями. Подрезали немного шум в логе от OC. Методы вроде [il]robot.suck[/il], [il]robot.suchFromSlot[/il] и [il]transpoer.transferItem[/il] теперь возвращают вместо [il]true[/il] число перемещённых предметов. Немного уменьшили назойливость частиц наномашинок. Жёсткий диск 3 уровня в режиме без ФС стал иметь по умолчанию не 6, а 8 пластин. Улучшили рендер кабелей как-то. Такие же "как-то" улучшения произошли с инвентарём роботов, апгрейдом крафта, методами [il]swing[/il] и [il]use[/il], взаимодействием с жидкостными баками. С модами получше работать должны. Чанклодыри можно ставить в микроконтроллер теперь. Расширили покрытие юникода шрифтом. Стандартный биос стал есть меньше памяти. Мониторы глючить должны поменьше. Пофиксили обнуление содержимого инвентарей блоков мода при крашах. Ещё некий краш при установке микроконтроллеров починили. Команду [il]/oc_nm[/il] вправили в место и заставили работать. Дюп роботов убран. Команды перемещения теперь говорят, успешно или безуспешно вызов завершился. Форсирование [il]LuaJ[/il] не форсировало эту архитектуру. [il]transferItem[/il] проверял не ту сторону. Починили Unknown error при попытке залить чего-то в некие машинки. Дюп дронов тоже починили. Выкорчевали возможную ошибку при запуске вместе с IC2. Роботы перестали потреблять ингредиенты при крафте, которые не потребляются. Апгрейд ангельский стал работать. Пофиксили торговый апгрейд. Его прямая задача исполнялась кривовато. Роботы не перемещались, когда нужно было. Дюп предметов дронами и роботами. Дискету network тоже можно ставить через install теперь. Дюп жидкостей, конечно, тоже был и тоже пофикшен. Дроны не реинициализировались после включения по сообщению модема. И вели себя очень странно. Всякие фиксы в интеграции с AE2. Опять некий дюп EEPROM. Удалён. Краши при загрузке с Applied Llamagistics. Краши при нетрадиионной установке компьютеров. Краши (но на клиенте), связанные как-то с кабелями и загрузкой чанков. [il]enableNanomachinePfx[/il] не имела эффекта. Роботы стали вызывать обработчики модов при получении опыта. Вводящие в заблуждение сообщения анализатора о выключенных компьютерах стали вводить в заблуждение в меньшей степени. Микроконтроллеры свою начинку теперь тоже выключают вместе с собою. Всякие ошибки кидал апгрейд поводка вместе с некоторыми модами. Фиксед. [1.10+] Починен рецепт крафта карточки с мировым датчиком. Экран планшетов теперь не зависает. Терминальные серверы ненормально цепляли удалённых терминалов на себя. Ошибки освещения с шейдерами. В OpenOS ещё отметить можно: Команда [il]reset[/il], которая ресетит разрешение. Ошибки сервисов пишутся в /tmp/event.log. Можно теперь ловить ошибки по Ctrl-Alt-C (жёсткие прерывания) путём переопределения функции в [il]process.info().data.signal[/il]. Копипаст в [il]edit[/il]: Ctrl-K — вырезать, Ctrl-U — вставить строку. Процессы закрывают файлы при завершении. Ссылочка на гитхаб, откуда можно скачать мод.

Fingercomp

Fingercomp

 

Обновление OpenComputers 1.7.2

Он вышел раньше, чем я предполагал — ниже список нового. Сила овец и оцелотов в их пушистости. Теперь пушистость можно приложить к делу и питать компы — с помощью ковровых конденсаторов. От обычных конденсаторов они толком не отличаются, но могут генерировать энергию, если по ним ходят минимум 2 пушистых животных: овцы или оцелоты, которые генерируют больше энергии. Все новые процессоры, которые будут скрафчены, будут с Lua 5.3 по умолчанию. Сменить можно так же — шифт-пкм. К беспроводной карточке, которую мы все знали, теперь добавили урезанную версию T1, тоже беспроводную. Она может открывать только 1 порт и стрелять сигналом на 16 блоков, а не 400. Креативная компонентная шина (штука, пихабельная в серверы), которая добавляет 1024 компонента. Логичное дополнение. Роботов можно подключать к компьютерам как компоненты. И менять имя роботов: то, что раньше делалось в наковальне, теперь можно через setName и getName. Робот должен быть выключен, чтобы функции работали. Починены всякие проблемки с рендерингом всяких символов. Блоки-инвентари иногда не сохраняли содержимое при сохранении мира. Дроны с чанклодырями не всегда грузили чанки. Пофикшена интеграция с AE2. computer.addUser неправильно отдавал ошибку как-то. Хитбоксы у кабелей теперь обтягивают их форму. Раньше кабели-пересечения были с хитбоксом на весь блок. Апгрейд крафта не всегда крафтил, когда должен был. Апгрейд крафта крафтил один предмет и ломал рецепт — для всех, в том числе игроков. Весело. Датчик движения как-то коряво работал. Пофикшена работа роботов с предметами-инвентарями вроде жидкостных ячеек IC2. Устранена возможная утечка памяти в сетевом коде. В MC 1.10+: пофикшена getMetadata у дебаг-карты. В MC 1.10+: добавлена getBlockstate для дебаг-карты. В MC 1.12: нельзя было заменить EEPROM дрону. В MC 1.7.10: добавлены getAllStacks и inventoryName для транспозеров с инвентарных апгрейдов. Обновлён французский перевод. В OpenOS: Обновлён install.lua, чтобы работал более предсказуемо. uuid.lua возвращает правильные UUID 4 версии, как в RFC написано. Фиксы всякие поддержки vt100. Утечка памяти при загрузке процессов (есть и такая, даже в Луа). Более конкретные комбинации клавиш: Ctrl+Alt+Delete не будет считаться за Ctrl+Delete, например.


Вайтлиста измерений для чанклоадера... ну, их пока нет.   Скачать.

Fingercomp

Fingercomp

 

Обновление OpenComputers 1.7.1

Багофиксы, в основном только они. Вот из того, что добавилось: У планшетов можно получать полноценное направление взгляда игрока. Количество максимальных частей пакета добавлено в информацию об устройстве (та, что computer.getDeviceInfo(). [1.10.2] Интеграция с ExtraCells и Mekanism. [1.12.2] Интеграция с ComputerCraft.

Остальное: Изменили рецепт алмазных кусков по умолчанию. Пофиксили область видимости датчика движения. Планшетам разрешили отрубать экран. Дроны адекватно заставили воспринимать чанклодырное улучшение. Item conduits из EnderIO чего-то из микроконтроллеров доставали ненужного. Несовместимость с IC2 Classic устранена. В IRC-клиенте с дискеты пофиксили CTCP. [1.11.2] Какая-то бага с добавлением предметов в улучшение-БД. [1.11.2] И ещё бага с доступом к компонентам вроде дисковода в планшетах.

Обновления в OpenOS: Нет необходимости теперь, в кои-то веки, писать = в начале строк в интерпретаторе Lua. Оно автоматически возвращает. Можно в error пихать таблицы, и крашиться не должно. Наконец-то разрешили монтировать системы файловые в существующие директории. Ещё можно примонтировать директорию в другое место. Если вы напишете одну команду и 10 раз другую, то в истории последняя будет только один раз. Не придётся 10 раз тыкать "вверх", чтобы первую команду получить. Фиксили проблемы с загрузкой OpenOS на медленных хостах. Я думаю, это ошибка TLWY, которая при старте кидалась. .shrc может принимать ввод. Пофиксили поиск названия клавиши по коду в либе keyboard. Фикс event.cancel и event.ignore какой-то. Интерпретатор теперь здраво воспринимает ошибки переполнения памяти в сериализаторе. Какой-то TLWY в /bin/tree.lua. Улучшения в vt100 всякие. Код стал ещё уродливее ради уменьшения потребления памяти. Вот такие улучшения.

Вот как-то так. Отсюда качабельно.

Fingercomp

Fingercomp

История одного дронодома

Многие игроки здесь видели или хотя бы слышали про огромный дронодом, который построил @Asior в былые времена на сервере RoboCraft. С тем чтобы прояснить происхождение этой хаты и оставить о ней заметку в этом клубе, специально для «Новостей подполья» @Fingercomp обратился к создателю постройки и попросил рассказать про неё. Редакция представляет обработанную версию истории.   История начинается в начале мая 2016 года, когда запустился сервер RoboCraft, на который сразу же хлынули толпы игроков, хотевшие «поскорее стать топовыми игроками, обладателями гор ресурсов и, конечно же, новых идей и программ». Туда попал и герой нашего рассказа. Развитие было довольно сложным. «Поначалу я хотел, как обычно, отстроить бункер и спокойно, потихоньку наращивать силы», но этому воспрепятствовал случай: система автоматического расселения игроков закинула Asior невесть куда — в середину заражённого биома. Очевидно, что герой этому не обрадовался. Ему потому пришлось бегать в поисках нового места.     Конечно, Asior таки организовал себе временное убежище и начал стремительное развитие в игре. Но в чате игроки часто оставляли ссылочки на скриншоты своих невероятно красивых палат с невероятно крутых ракурсов.     Он перерыл огромное число чертежей домов, замков, статуй — и решил построить дрона. Дрона из OpenComputers. Ведь сервер специально разрабатывался для этого мода. Asior зашёл в сингл и долго, упорно воздвигал новые варианты постройки и безжалостно крушил старые. Наконец, он определился с тем, как именно должно будет выглядеть его будущее жилище. Оставалось лишь воспроизвести это всё на сервере. Но здесь и возникла основная проблема: как добыть такое огромное количество ресурсов для строительства? Разрешена она была путём не самым чистым:     Впрочем, и того, что он раздобыл, сполна хватило на постройку основного корпуса дрона. Это потребовало огромного числа строительных лесов и невероятных акробатических способностей и дополнительно осложнялось тем фактом, что полученные вечные блоки не перемещались из хотбара. Но стиснув зубы и получая подкормку от щедрых игроков Asior таки построил дрона.       Потому пришлось придумать, как расширить жилище. Некоторые предлагали соорудить какое-нибудь здание, к которому был бы «привязан» дрон, но, увы, это не вписывалось в местность.     Далее настала очередь внутренней отделки: ставились перегородки, размещалось оборудование. А монументальное сооружение, памятник роботу и дрону, стал пользовался большой популярностью, чему создатель не противился: «я был не против, чтобы все желающие посмотрели, как я живу, уточнили какие-то вопросы или помогли чем-нибудь».   С тех пор сервер RoboCraft давно закрыт, но память о роботе и дроне жива до сих пор. Редакция присоединяется к пожеланию героя остроить то, что поражало бы воображение и отпечаталось в приятных воспоминаниях десятков игроков.   И мы всё так же мы призываем вас оформить подписку на «Новости подполья». Годноты здесь много было, есть — а то ли ещё будет.

Fingercomp

Fingercomp

Дискорд

Всем ку! Наконец-то форум ожил и соответственно выползли мы из подполья 😃 Приглашаю всех желающих и участников форума в наш канал в Discord! Инвайт - ссылка в наш канал в Discord : https://discord.gg/tpjbgjG Заходите, общайтесь. Там (иногда) бывает мой бот, который мост между каналом в IRC и Discord, так что иногда появляется возможность переписываться с ирковчанами 😃

MeXaN1cK

MeXaN1cK

 

Автокрафт OpenComputers

Первая публичная реализация автокрафта на OpenComputers.
Исполнительным элементом является робот, командующим же — компьютер. Хранилищем предметов здесь выступает МЭ-сеть, с интерфейсом в роли передатчика предметов в обе стороны.
Для начала использования автокрафта Вам потребуется:

Компьютер. Это главная часть системы, хранящая базу данных рецептов и экспортирующая предметы из дерева крафта в нужном порядке. Требования: Графическая карта второго уровня. Беспроводная сетевая карта. Процессор второго уровня и выше. Планки памяти уровня 2 и выше (зависит от размеров базы данных). Жёсткий диск уровня 1 и выше (зависит от размера базы данных). Интернет-карта (для скачивания программы). EEPROM. OpenOS Робот. Это исполняющая часть системы. По сигналу с модема "craft" она крафтит предметы и складирует полученное в МЭ. Требования: Апгрейд крафта. Контроллер инвентаря. Инвентарь. Клавиатура. Экран Т1. Дисковод. Интернет-карта (для скачивания программы). Беспроводная сетевая карта. Процессор уровня Т2 и выше. Планки памяти уровня Т2 и выше (возможная комбинация: Т2 и Т1.5). EEPROM. OpenOS. Жёсткий диск первого уровня. МЭ-сеть.
Это хранящая часть системы, из которой достаются айтемы и в которую кладутся результаты крафтов. Требования: ME Drive и ячейки. Терминал для доступа к сети (может быть исключён). Интерфейс. После крафта всех необходимых вещей можно приступать к установке. Поставьте робота лицом в интерфейс. Убедитесь, что интерфейс готов к работе. Теперь соберите компьютер. Установите на робота и компьютер OpenOS. Скачайте программы, используя команды ниже, для робота и компьютера соответственно:
Компьютер: pastebin get pXunJUE2 /usr/bin/craft.lua pastebin get ixwtEUr6 /usr/bin/recipes.lua pastebin get V2Zrnp6F /usr/share/db Робот: pastebin get tiwidCYt /autorun.lua pastebin get S1J5Y7mb /scan.lua Теперь запишите адреса сетевых карт на компьютере и роботе (components modem). Откройте файл /usr/bin/craft.lua на компьютере.
В строке ROBOT замените значение на адрес сетевой карты робота.
В строке DIR замените значение на сторону экспорта (сторона света, где находится робот относительно интерфейса). "north", "south", "east", "west", "up", "down".
В строке TECH_SLOTS замените значение на количество слотов внизу робота (инструмент, дискета, контейнеры).

После этого откройте файл /scan.lua на роботе и замените значение переменной COMP на адрес сетевой карты компьютера.


Если всё сделано правильно, можно запустить файл /autorun на роботе и recipes на компьютере. Интерфейс у данных программ понятен без моих комментариев. Программа recipes предназначена для управления базой данных: удаление, изменение, добавление, просмотр рецептов. Программа craft на компьютере предназначена для самого процесса крафта. Напоследок, для сканирования рецептов нажмите 7 в recipes, выложите рецепт в роботе и в выделенный слот положите результат крафта. Затем запустите программу scan на роботе и выполните инструкции на компьютере.

Скриншоты.
  Все вопросы, замеченные баги оставляйте в комментариях.

Fingercomp

Fingercomp

 

Автозапуск программ

Если до версии 1.6 все использовали файл /autorun.lua и были довольны, то теперь ситуация несколько изменилась. Поэтому я опишу все варианты автозапуска программ в этой небольшой заметке.   С версии OpenOS 1.6 файл autorun.lua больше не запускается на rootfs (то есть на файловой системе работающей операционной системы). Вот все пять способов, которые можно использовать для автозапуска программ. Модифицировать /init.lua.
Это самый плохой и ужасный вариант из всех. Во-первых, программа будет запускаться до запуска шелла и инициализации библиотек, поэтому возможны краши системы. Во-вторых, если сделать ошибку в файле, то придётся переустанавливать этот файл, что не очень удобно.
Добавить скрипт в /boot.
Это не такой плохой вариант, но здесь также возможны ошибки при использовании стандартных библиотек, так как бутскрипты запускаются не в самом конце загрузки.
Модифицировать /etc/profile.
Это файл, каждая строка которого последовательно исполнаяется при запуске программ. Проблема в том, что при переустановке системы этот файл будет перезаписываться. Поэтому не вариант.
Модифицировать /home/.shrc.
Это самый оптимальный вариант. Но программа будет запускаться при каждом запуске шелла. Если прописать exit в шелле, то программа запустится ещё раз. Если для графических всяких программ это самый лучший вариант, то для одноразовых демонов, которые регистрируют листнеры на ивенты и выходят, вариант не очень хороший, так как тогда листнеры зарегистрируются дважды.
Использовать систему rc.
Подробно о ней рассказывал @LeshaInc: http://computercraft.ru/topic/1679-rc-chto-za-zver-takoi/
Это система, которая позволяет писать своих "демонов" — программ, исполняемых в фоне — и контролировать их из шелла с помощью команд. Графические утилиты так запускать проблематично, потому что возможны всякие артефакты отображения.

Поэтому используйте варианты 4 или 5 в зависимости от программы, которую требуется запустить.

Fingercomp

Fingercomp

Upong

Меня тут попросили что-нибудь написать в блог. Например про мои заброшенные проекты. Честно перелопатил свои репозитории, но не нашел ничего интересного. Ни одного интересного заброшенного проекта. Просто беда какая-то. Даже на секунду померещился нимб над головой (такой наверняка есть у любого программиста, который всегда дописывает все свои проекты до единого). Но потом я пошел посмотрел в зеркало и морок пропал.   Поэтому вот скриншотик из пинг-понга, который я начал было писать, чтобы потренироваться в работе с сетью (хотел приделать туда мультиплеер). Но дальше фотошопа (точнее гимпа) меню у меня дело не ушло, писать одному было скучно, и быстро нашлись дела по-прикольнее.     Сохранился даже репозиторий: https://github.com/MoonlightOwl/Upong

Totoro

Totoro

 

Sound Card / спектр, преобразование Фурье, PCM и WAV

В звуковой карточке есть дохрена функционала - поэтому она и крутая. В этой части попытаюсь объяснить достаточно сложные штуки, которые используют большие дяди. Надеюсь, что вы прочитали и поняли две предыдущие части цикла - это будет довольно важно для последующего повествования. [Раньше тут был полноценный пост с эмбедом, но после переезда оно всё, соответственно, сломалось. Текст доступен здесь.]

Fingercomp

Fingercomp

 

Sound Card / как сгенерировать простой звук

Перед тем, как я начну, хочу сразу обратиться к забугорным ребятам, читающим эту запись.   Hey! If you are reading this, please tell Vexatos to document the sound card. The in-game manual page is a meaningless piece of text that explains nothing.
Documentation is written to help others to get an idea of how something works. The sound card is a complex thing that definitely needs a how-to guide to be used in programs.
So if he wants the sound card to be used in programs, he should write the proper documentation with the detailed description of each concept, feature, and component method.
The following is the result of efforts put to understand how the sound card works. It took me several months until I realized how to make a simple beep. How am I supposed to create more complex waves? >_>   Ну да ладно.  
Хей! Разбирался я недавно, как работает звуковая карта из CX, и поэтому хочу предоставить результаты своих трудов.
Как и написано сверху, доков нет, автор мода бездействует, везде уныние и отчаяние, поэтому пришлось действовать по-хардкорному. То есть чтением исходников и научнейшим тыком.   Итак, есть компьютер со звуковой картой. Нужен звук. Звучит просто, м?     Звуковая карта даёт нам замечательные функции вроде установки ADSR-огибающей, типа волны, шума простого или из LFSR, амплитудной и частотной модуляции. Но мы тут решили заняться простым звуком, поэтому всякие LFSR, AM, FM безжалостно выкидываем. Получаем такой план: Открыть канал. Поставить тип волны. Задать частоту волны. Установить громкость. Обработать.
  1. Открыть канал
Э, извините, а что за каналы?
...
В звуковой карте звук генерируется с помощью инструкций, которые выполняются друг за другом. Почти все работают с определённым каналом. Если бы канал был один, мы бы смогли играть в один момент времени только одну, условно, ноту. Чтобы параллельно шло сразу несколько звуковых волн, нужно использовать несколько каналов. На каждом можно задать свои тип и частоту волны, громкость, ADSR-огибающую. По умолчанию таких каналов в звуковой карте 8 штук.   С каналами всё довольно просто: открытый канал генерирует звук, закрытый канал не генерирует звук (правда, нужно будет здесь кое-что поправить, когда будет изучать ADSR).   Открыть канал можно с помощью функции sound.open(channel: number). Закрыть: sound.close(channel: number).   2. Поставить тип волны
Всего типов звуковая карта поддерживает пять: шум (noise), меандр (square), синусоида (sine), треугольная (triangle) и пилообразная (sawtooth).  

Waveforms ru [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0) или GFDL (http://www.gnu.org/copyleft/fdl.html)], автор Omegatron (original work)
Kirill Borisenko (Russian translation; see File:Waveforms for Cyrillic Segment.png)
Incnis Mrsi (ultimate work), с Викисклада
Очевидно, что разные типы волн звучат по-разному.   Установить тип волны можно с помощью функции sound.setWave(channel: number, type: number). Видно, что тип волны должен быть числом. Соответствующее типу волны число можно получить с помощью таблицы sound.modes. Например, sound.modes.sine или sound.modes.noise.   3. Задать частоту волны
Чем больше частота, тем выше звук. Частота 440 Hz соответствует ноте ля в первой октаве. Другие частоты можно найти по табличке на Википедии.   Используем функцию sound.setFrequency(channel: number, freq: number).   4. Установить громкость
Громкость может быть в пределах от 0 до 1 включительно. Половина максимальной громкости - 0.5. И так далее.   Функция: sound.setVolume(channel: number, volume: number).   5. Обработать
Чтобы обработать все инструкции, нужно вызвать sound.process(). Но если вы просто возьмёте и вызовете функцию, то ничего не услышите. Дело в том, что, достигнув конца инструкций, звуковая карта прекращает генерировать звук.   Поэтому нужно выставить некоторую задержку в конце всех других инструкций, чтобы звуковая карта подождала перед прекращением воспроизведения звука. Для этого служит sound.delay(delay: number). Задержка указывается в миллисекундах. Например, sound.delay(2500). При этом эта функция тоже является инструкцией, поэтому её можно выставить несколько раз. Однако если задержка превышает 5000 (5 секунд), то звуковая карта выдаст ошибку.     Проиграв несколько типов волн, мы всё равно останемся неудовлетворёнными. Ну где это видано, что звук мгновенно нарастает и затухает?
Чтобы как-то это контроллировать, мы воспользуемся ADSR-огибающей. На Википедии довольно хорошо написано, в чём смысл этого.   Скопируем сюда оттуда пикчу:

И вкратце распишем суть.
Если вы хоть когда-либо слышали музыкальные инструменты, вы могли заметить, что громкость звука со временем меняется. У гитары громкость мгновенно нарастает и постепенно затухает, а у флейты нарастает не сразу, например.
Чтобы очень-очень грубо моделировать эти инструменты, мы используем ADSR.
Attack - это время нарастания громкости звука до максимальной (последняя устанавливается через sound.volume).
Decay - время затухания сигнала сразу после достижения максимальной отметки до следующего параметра.
Sustain - отметка громкости, к которой стремится Decay. Значение 1 - это максимальная громкость звука на канале (установленная с помощью sound.setVolume), 0 - отсутствие звука вовсе.
При достижении отметки Sustain звук остаётся на этом уровне до "отпускания клавиши" (т.е. закрытия канала).
Release - время затухания сигнала после закрытия канала, от отметки Sustain до нуля.   Время везде в миллисекундах.   Попробуем? Вызываем функцию sound.setADSR(channel: number, attack: number, decay: number, sustain: number, release: number) до пятого шага. Например sound.setADSR(1, 1000, 500, 0.33, 1000).
И замечаем, что громкость меняется со временем. Yay!     На этом счастливом моменте повествование обрывается. Итоговый код, который можно запустить и послушать примитивный бип: local sound = require("component").soundsound.close(1)sound.close(2)sound.open(1)sound.setWave(1, sound.modes.sine)sound.setFrequency(1, 440)sound.setVolume(1, 1)sound.setADSR(1, 1000, 500, 0.33, 1000)sound.delay(1000)sound.open(2)sound.setWave(2, sound.modes.noise)sound.setFrequency(2, 440)sound.setVolume(2, 0.6)sound.setADSR(2, 1, 250, 0, 1)sound.delay(1500)sound.process()
К остальному, надеюсь, я ещё вернусь в другой записи. Сначала нужно самому понять, как оно работает в версии для CX.
Поэтому наслаждаться пока приходится только бибикалкой.

Fingercomp

Fingercomp

 

Sound card / звуковые волны, модуляция

Продолжаю рассказывать про Computronics и, в частности, про офигенную звуковую карточку из этого мода. На очереди модуляция: частотная и амплитудная. Помимо этого восполняю долг по основам.   Юзать будем мою прогу synth, которую я недавно зарелизил. Она здесь невероятно поможет.   Звуковая волна
Вы же знаете, как выглядит звуковая волна?  
 
Вот, например, синусоида. Как видно, здесь есть некоторый фрагмент, который повторяется несколько раз. Частота показывает, сколько раз в секунду этот фрагмент повторяется, и измеряется в герцах (Гц или Hz). Чем выше частота, тем больше волна "сжата", скажем так, с боков. Вот как выглядят три синусоиды с разными частотами: 110 Гц, 220 Гц, 440 Гц - на одинаковом масштабе.  
 
Кроме частоты, звуковая волна характеризуется таким параметром, как амплитудой. Это, скажем так, расстояние от нуля до самого большого по модулю значения волны. Чем больше амплитуда, тем громче звук. Мы примем за единицу амплитуду сигнала на звуковой карте при максимальной громкости.
При нулевой амплитуде на графике будет прямая линия вдоль горизонтальной оси.  
 
При максимальной громкости у простой синусоиды пиковые значения будут в точках +1 и -1. Они плавно сменяются. Прикольно, да.   Синусоиды - это офигенные штуки, но на них клин светом не сошёлся. Из основных типов волн, помимо синусоид, есть меандр, треугольная волна и пилообразная. Они в таком же порядке изображены на рисунках ниже.  

(по поводу первой и третьей пикчи: вообразите, что волны не разомкнуты, а просто резко переходят с одного конца на другой. Небольшая хрень, над которой нужно побиться.)  
На самом деле, их можно представить в виде набора бесконечного числа синусоид, о чём когда-то поведал Фурье, но мы с этим заморачиваться не будем: раскладывать на синусоиды в звуковой карте не принято. Поведали об этом для красного словца - и то хорошо.   Теперь, когда рассказал про основы, можно переходить к более весёлым и сложным вещам. То бишь к модуляции.   Модуляция
Модуляция - это изменение одного сигнала (несущего) другим (модулятором). Изменять можно по-разному - мы будем говорить об амплитудной модуляции и частотной модуляции.
С модуляцией у нас появляется уникальнейший шанс получить офигенные и красивые звуки, поэтому не будем ждать и сразу перепрыгнем к мясу.   Амплитудная модуляция
Как я сказал, для модуляции нужны два сигнала: несущий и сам модулятор. Поэтому здесь и далее я привожу на рисунках сразу три графика: несущий сигнал, модулирующий сигнал и результат модуляции.
Например, выставим две синусоиды с частотой 440 Гц.  
 
Итак, амплитудная модуляция - это умножение одного сигнала на другой.  
A(t) = C(t) × M(t),  
где t - время, C - функция, возвращающая значение несущей волны на моменте времени t, M - то же, но для модулятора.
Однако не всё так просто. Перед умножением к значениям с модулятора прибавляется единица. Получается, что самая верхняя точка будет на +2, а самая нижняя - на 0. Иными словами, волна перенесена вверх.   На низких частотах - до 20-30 Гц, откуда начинается граница слышимого человеком звука, - графики будут выглядеть как-то так, медленно увеличивая амплитуду от 0 до 4 и обратно.  
 
И звучать оно будет как увеличение и уменьшение громкости (количество таких увеличений и уменьшений равно частоте модулятора).   Однако когда частота модулятора становится больше, наблюдаем вот такую картину (частота модулятора равна здесь 330 Гц).  
 
Если прислушаться, то станет заметно, что одновременно будто бы проигрываются три звуковых волны. Одна волна имеет такую же амплитуду и частоту, как и несущая. Две другие частоты, задающие боковую полосу частот (диапазон частот, сконцентрированных рядом с какой-либо), находятся так:  
f1 = |c - m|,
f2 = c + m,  
где c - частота несущей волны, а m - частота модулятора. Возникающие звуковые волны около этих частот вдвое тише, чем несущая.   В звуковой карте можно поставить амплитудный модулятор следующим образом: sound.setAM(carrierChan: number, modulatorChan: number).   Амплитудная модуляция - забавная вещь, и в то же время тут очень трудно подобрать что-то интересное и красивое. Поэтому переходим к частотной модуляции - там веселья дофига.   Частотная модуляция
В частотной модуляции модулятор изменяет частоту несущей волны. Как ни странно.
Почему частотная модуляция уделывает амплитудную? Здесь может быть гораздо больше боковых частот. И потому звучать оно может гораздо круче.   При когда значение на модуляторе увеличивается, повышается и частота на несущей волне.   Звуковая карта поддерживает индекс модуляции. Он задаёт максимальное изменение частоты несущей волны. При индексе, равном 100, частота несущей волны может меняться на 100 Гц вверх и на 100 Гц вниз. Если индекс равен 1000, то частота меняться может на 1000 Гц вверх и на 1000 Гц вниз. Ну и так далее.
Иными словами, индекс задаёт силу модуляции.   Если частота модулятора будет очень низкой (например, 4 Гц), то получим что-то вроде сирены.  

c = 440 Гц; m = 4 Гц; i = 200  
С повышением частоты получим вибрирующий звук. И потом услышим дополнительные частоты.  

c = 220 Гц; m = 880 Гц; i = 660  
Как видно, получившаяся волна получилась довольно сложной.   Установить модулятор в звуковой карте можно с помощью функции sound.setFM(carrierChan: number, modulatorChan: number, index: number).   И не забывайте про ADSR-огибающую: из однообразного тона можно получить довольно интересный звук. Как работает эта штука, я рассказывал.     На этом всё. Из всех фич остался неразобранным лишь шум LFSR, но там штука очень и очень странная и непонятная.   Наверняка всё равно остались некоторые вопросы по поводу модуляции. Поэтому за дополнительной информацией я предлагаю обратиться к другим сайтам. Вот несколько очень полезных ссылочек, где есть примеры звука и детальное описание: FM Synthesis - The Synthesizer Academy Frequency modulation synthesis - Wikipedia FM Synthesis - Music and Computers Modulation synthesis - Wikibooks (здесь рассказывается про амплитудную модуляцию в том числе).

Ну и используйте прогу synth, чтобы удобно было изучать звуковую карту.

Fingercomp

Fingercomp

 

OpenOS. Потоки

Потоки — очень полезные штуки, позволяющие исполнять несколько кусков кода. Раньше для их использования приходилось скачивать отдельную библиотеку, работающую через костыли. Начиная с OpenOS 1.6.4, они есть в стандартной поставке ОС — в модуле thread. Давайте посмотрим, из чего она состоит — и в чём её преимущество перед любыми другим библиотеками.   Начнём с версий. OpenOS 1.6.4 — версия, включённая в OpenComputers 1.7.0. Если не хотите возиться с обновлением системы вручную, требуется иметь версию выше или равную 1.7.0.   Сразу обращаю внимание на самую важную вещь: потоки не могут исполняться одновременно. В один момент времени только один поток может работать.   В чём тогда красота тредов?   Они автономны, то есть: Начинают исполнение сразу же после создания. Передают исполнение в другие потоки в местах, указанных использователем, — при том или ином вызове computer.pullSignal (os.sleep, event.pull и т. д.). Автоматически продолжают своё исполнение без необходимости самостоятельно их стартовать. Потоки можно убить и приостановить.

Они неблокирующие: Вызов computer.pullSignal не блокирует исполнение других потоков.

Они отцепляемые: Процесс, в котором был создан поток, называется родительским. При завершении родительского процесса все потоки останавливаются. Поток может отсоединиться от родительского процесса и работать полностью автономно — например, как слушатели событий. Поток может сменить родителя на другого. Поток сам является процессом и потому может создавать дочерние потоки. Работающий поток не даёт завершиться своему родителю.

Они независимы при обработке событий: Потоки не наследуют и не передают дочерним свой набор слушателей событий. Все слушатели событий и таймеры принадлежат только конкретному потоку. Как следствие, поток не может изменять их набор в другом. Слушатели и таймеры автоматически удаляются при завершении потока, даже если завершение вызвано ошибкой. Приостановленные потоки игнорирует события. Если несколько потоков вызвали event.pull на одно и то же событие, они оба его получат.

Этот набор фич в таком объёме присутствует только в этой библиотеке, и ни одна другая и не даёт столько простоты в работе с ними.   Пожалуй, приступим к использованию. Потоки создаются функцией thread.create: первым аргументом передаётся функция, дальше идут аргументы к ней. local thread = require("thread")local t = thread.create(function(a, b) print("В потоке получены аргументы:", a, b)end, 21, 42)
Функция возвращает объект потока. Его же может получить сам поток вызовом thread.current() — однако если вызвана не в потоке, то возвращает nil. На всякий случай, основной процесс не является потоком.   Объект потока позволяет чудить различные вещи с потоком.   t:suspend() приостанавливает поток. Как уже сказано, такой поток не будет получать события и обрабатывать тики таймера. Забавно, что если приостановить поток, когда он ждёт события, то неизвестно, что он получит после его возобновления.   t:resume() возобновляет работу ранее приостановленного потока. Так как созданные потоки сразу начинают работу, то обычно этот метод вызывать не придётся.   t:kill() убивает поток, то есть завершает его, удаляя всех слушателей и таймеры. Возобновить работу потока после того, как он убит, нельзя.   t:status() возвращает строку со статусом потока: "running" — поток работает или блокирован другим. Такой поток не даёт завершиться своему родителю. "suspended" — поток приостановлен. Его дочерние потоки также будут приостановлены. Когда родительский процесс завершается, такой поток автоматически убивается. "dead" — поток мёртв.
  t:attach() позволяет сменить родителя у потока. Без аргумента поток будет присоединён к текущему процессу. Переданное как аргумент число позволяет указать, к кому присоединить: 0 — текущий процесс, 1 — родитель текущего и т. д.   t:detach() отцепляет поток от родителя. Такой поток будет работать до его остановки или перезагрузки компьютера.   t:join() останавливает процесс, в котором была вызвана это функция, до завершения потока t. local thread = require("thread")local t = thread.create(function() os.sleep(10)end)t:join() -- остановится на 10 секунд
Можно передать первым аргументом этой функции число, которое будет служит таймаутом (в секундах). Тогда, если не успеет завершиться поток за это время, join завершится досрочно.   t:join ждёт только одного потока. Для групп потоков есть функции thread.waitForAny и thread.waitForAll — обратите внимание, что это функции библиотеки, а не методы объекта потока.   Обе функции первым аргументом требуют таблицу с потоками, а вторым опционально можно задать таймаут.   thread.waitForAll ждёт, пока завершатся все потоки из списка. local thread = require("thread")local t1 = thread.create(function() os.sleep(10)end)local t2 = thread.create(function() os.sleep(15)end)thread.waitForAll({t1, t2})print("Это сообщение будет написано через 15 секунд")
thread.waitForAny ждёт, пока завершится хотя бы один поток из списка. local thread = require("thread")local t1 = thread.create(function() os.sleep(10)end)local t2 = thread.create(function() os.sleep(15)end)thread.waitForAny({t1, t2})print("Это сообщение будет написано через 10 секунд")
Что будет, если поток бросает ошибку? При ошибке в потоке она не будет проброшена в родительский процесс. Как и со слушателями, она будет записана в файл /tmp/event.log, но родитель не сможет узнать причину ошибки — и, вообще, успешно ли завершился поток. local thread = require("thread")local t = thread.create(function() os.sleep(3) error("test")end)print(t:status())--> runningt:join()print(t:status())--> dead
Кроме того, событие жёстокого прерывания (Ctrl+Alt+C) не передаётся всем процессам — только одному; причём неизвестно, какому именно: родителю или одному из его потоков. Если вы используете потоки, первым делом сделайте один, который будет ждать события interrupted и подчищать ресурсы. local thread = require("thread")local cleanupThread = thread.create(function() event.pull("interrupted") print("Принял ^C, чищу всякие ресурсы")end)local mainThread = thread.create(function() while true do local input = io.read() if input == "exit" then break end endend)thread.waitForAny({cleanupThread, mainThread})os.exit(0)
Обратите внимание, что в конце программы стоит os.exit. Где-то я уже упоминал не раз, что родительский процесс, достигнув конца программы, не завершится до тех пор, пока работает хотя бы один из его дочерних потоков. Вызов os.exit() позволяет выйти из программы, закрыв все дочерние потоки. Что, безусловно, достаточно удобно.  
Есть ещё один момент. Допустим, данная программа запускается в роботе: local robot = require("robot")local thread = require("thread")local moveThread = thread.create(function() while true do robot.forward() endend)local inputThread = thread.create(function() while true do local input = io.read() if input == "exit" then break end endend)thread.waitForAny({inputThread, moveThread})os.exit(0)
Если вы запустите эту программу, то должны заметить, что вы ничего не сможете написать в роботе, хотя работает io.read. Дело в том, что функция robot.forward вызывает метод компонента, который блокирует исполнение компьютера. Пока робот двигается, на компьютере не может выполняться ни одна команда.   Чтобы хоть что-то можно было вставить в строку, то поставьте после robot.forward какой-нибудь os.sleep(0) — он позволит соседнему потоку принять и обработать события. Тем не менее, строка ввода всё равно будет работать с тормозами.   В подобном случае задумайтесь над тем, чтобы использовать вместо строки ввода иное средство коммуникации: редстоун, сеть, интернет-сокет.   Несмотря на всё, библиотека действительно облегчает работу с потоками в OpenOS. Кроме того, очень удобно поместить все слушатели событий в один поток, чтобы они все автоматически были удалены после убийства потока. local event = require("event")local thread = require("thread")local mainThread = thread.create(function() event.listen("key_down", function(evt, addr, key, code, user) print("A key has been pressed!") end) while true do print("do something") os.sleep(0.5) endend)-- событие interrupted не ловится обработчикамиlocal intThread = thread.create(function() event.pull("interrupted")end)thread.waitForAny({mainThread, intThread})os.exit(0)
Не нужно функции сохранять в переменные и помнить, что нужно ставить event.ignore в конце программы; не требуется ребутать компьютер, если программа завершилась с ошибкой, а до отключения слушателей дело не дошло.   В общем, красота.

Fingercomp

Fingercomp

 

OpenOS. От дуба до Мастера. Часть четвёртая. [||·]

Продолжаем беседу об операционной системе Bolge OpenOS. В этой записи речь пойдёт про те самые оставшиеся утилиты, которые облегчат жизнь программисту.

Сложность: средне 60%
Скучность: высокая 80%
Дубовость: для продвинутых 65%

Операционная система OpenOS в первую очередь покрывает вопросы (относительно) удобного программирования для OpenComputers. Конечно, это не исключает сторонние редакторы типа Sublime Text или Notepad++, но иметь такие средства нужно и важно. Давайте же я расскажу о них. 
УТИЛИТЫ ДЛЯ ПРОГРАММИРОВАНИЯ.
Простой перечень утилит с пояснениями.   address
Наипростейшая из наипростейших утилит, может соперничать даже с print("Hello, world!"). Просто выводит адрес компьютера. Интереса ради, пропишите view /bin/address.lua.   components
Ещё раз я расскажу об этой программке. Она выводит список компонентов, подключённых к системе. Можно задать фильтр, тогда выведутся только указанные компоненты. Если же указать ключ -l (о них в следующей части), то для каждого компонента будут указываться методы.   dmesg
Данная программа случает все события и выводит информацию о них на экран. Можно указать фильтры событий для просулшивания через пробел.   flash
Та самая программа, которая записывает код на EEPROM. Ключ -l выводит код о текущего EEPROM, -r записывает код в файл, а -q заставляет не задавать вопросов.   hostname
Данная программа бессмысленна без пакета network. Но, тем не менее, она может устанавливать и выводить текущее имя компьютера.   lua
Если запустить без аргумента или с ключом -i, то запустится интерактивный сеанс Lua-интерпретатора, где можно запускать программы. При этом все библиотеки автоматически переносятся в _G, так что дополнительно подключать их не требуется. Если же указать файл, то скрипт предпримет попытку запуститься и выдаст сообщение при обнаружении ошибки.   primary
Синтаксис: primary <компонент> [адрес]. Если аргумент [адрес] опущен, то выводится информация о первичном компоненте, иначе — предпринимается попытка сделать данное устройство первичным.   rc
Программа управления сервисами (о них — в следующих частях). Синтаксис: rc <сервис> [команда] [аргументы].   redstone
Предоставляет простой интерфейс управления редстоун-сигналами на первичной ред-карте/ред-блоке. По умолчанию синтаксис следующий: redstone <сторона> [значение]. Если [значение] не задано, выводится информация о текущем. Иначе — устанавливается.
Для многожильных кабелей синтаксис следующий: redstone -b <сторона> <цвет> [значение]. Работа необязательного аргумента такая же.
Для блоков, предоставляющих интерфейс беспроводной передачи ред-сигнала, команды следующие: redstone -w [сила]. Если [сила] задана, устанавливается значение на беспроводном передатчике. Иначе — выводится текущее значение силы. redstone -f [частота]. Устанавливает частоту, если задан аргумент, иначе — выводит текущую.

sh
Запускает сеанс командной строки, если не передано аргументов и io.stdin не перенаправлен. Иначе — читает команды из этого потока и выполняет их.   umount
Синтаксис: umount <точка монтирования>. Отмонтирует устройство по данной точке монтирования.
Вот так. Следующая часть целиком и полностью посвящена шеллу, ему родимому. Это: переменные окружения, алиасы, ключи, аргументы, особенности работы шелла и прочее. Пока жду комментариев по этой части, пишите, если что-то непонятно.  
← →

Fingercomp

Fingercomp

 

OpenOS. От дуба до Мастера. Часть третья. [||·]

Третья часть мануала по OpenOS, где я расскажу об идее монтирования файловых систем, а затем разбавлю повестью о пайпинге.

Сложность: средне 60%
Скучность: средне 55%
Дубовость: для продвинутых 50%
 
ФАЙЛОВЫЕ СИСТЕМЫ, ЗАКЛЮЧЕНИЕ. УСТРОЙСТВА И МОНТИРОВАНИЕ
Приступаем к самой сложной штуке в OpenOS — это монтирование файловых систем. Итак, начнём.  
Логика работы
Итак, начнём с того, что каждый накопитель — это устройство. У устройства есть свой адрес, который показывается при наведении мышкой. Устройство имеет определённую вместимость. Имеет количество свободной и занятой области. И, наконец, характеризуется наличием метки "Read-Only".

Допустим, это устройство У имеет адрес Address, вместимость 2 МБ и занято 0.5 МБ.
Другое устройство Д имеет адрес sserddA и такие же характеристики.
В OpenOS выбирается одно основное устройство, которое становится корнем. У нас / = У. Оно имеет все стандартные файлы и папки. Но как перейти на устройство Д? Оказывается, в папке /mnt/ собраны ссылки на все устройства. Именами ссылок являются первые 3 буквы адреса устройства. Что интересно, даже если У — корень, ссылка на устройство всё равно будет в /mnt. Вот так: / Устройство У|│┅│+ mnt | |+ Add Устройство У |+ sse Устройство Д |┅
Монтирование
Но что, если если у нас таких устройств — 5, например? Помнить все 5 адресов, пусть даже трёх первых букв, писать длинные пути — не-у-доб-но. Согласитесь? Поэтому в ОС есть возможность ручного монтирования. То есть, проще говоря, можно создать ссылку на устройство в любом желаемом месте. Вот только ln здесь не поможет — это ведь не файл, а совершенно другой раздел. Для этих целей служит команда mount. У неё два варианта работы: mount -a <адрес> <место назначения> — создаёт ссылку на устройство, адрес которого начинается с <адрес>, в заданной директории <место назначения>. При этом указаннной папки не должно быть до этого на диске. После этого в выбранной директории будут все файлы с устройства. Для устройства Д — mount -a sse /devD/. mount <путь /mnt/адрес> <место назначения> — также создаёт ссылку на устройство в месте назначения, только вместо адреса используется путь типа /mnt/адрес. Для устройства Д это — mount /mnt/sse /devD/.

Директория, которая является ссылкой на устройство, наызвается точкой монтирования.  
df
Эта команда отобразит все подключённые в данный момент хранилища данных, укажет точку монтирования и состояние. Для нашего компьютера было бы показано следующее: Filesystem Used Available Use% Mounted onAddress 512k 2M 25% /Address 512k 2M 25% /mnt/addsserddA 512k 2M 25% /mnt/sse
Как видите, если у файловой системы несколько точек монтирования, то будет показана информация о каждой из них.  
label
Это у нас названия простые и запоминающиеся. В реальности вместо них были бы эти ужасные длинные непонятные адреса. И чтобы сделать их понятными, можно повесить метку. Что проще: "835f48a-5df9-eb6a-36cb-6ab452d8f16a" или "Programs"? Думаю, всё же второе.
Чтобы поставить такую метку, воспользуемся командой label, у которой опять два варианта работы: label -a <метка или часть адреса> <метка> — устанавливает данному диску метку. label <точка монтирования> <метка> — устанавливает диску по данной точке монтирования метку.

Кстати, если не указывать метку, то выведется информация о текущей.
И ещё, установочная дискета имеет метку "openos", а жёсткий диск с ОС — "OpenOS" по умолчанию.
Информация При монтировании первым способом можно вместо части адреса ввести метку.       ПАЙПИНГ
Файлы
Простейший пример — перенаправление вывода echo в файл. Для этого используется >. Смотрите: echo "Hello all!" > hi.all. Содержимое hi.all очищается (или создаётся чистый файл, если его не было), и весь вывод идёт туда. Таким образом, в файле будет следующее: Hello all!
Если же необходимо из файла содержимое вывести в команду, используется команда <. Вот только примеров такому мне найти не удалось.  
Между командами
Другой простейший пример — команда cat. Если Вы помните, она печатала содержимое всё подряд, и если его больше высоты экрана, то просто обрезается. Так вот, чтобы не случалось такого, используется команда more и пайпинг.
Сразу скажу, как это сделать. more | cat <файл>. Обратите внимание на |. Этот символ обозначает, что весь вывод из правой команды надо перенаправлять в левую.
Скажу по секрету — тут можно было обойтись даже без пайпинга. more умеет сама открывать и читать файлы. Но есть нам нужен не файл, а, например, какой-нибудь df — вот тут и потребуется пайпинг.   В любом случае, эта штука является невостребованной, так как она недоделана. Вероятно, в будущем этот недостаток будет устранён, а пока просто запомним три оператора.

Список терминов: монтирование — процесс создания ссылки на устройство точка монтирования — название ссылки на устройство метка — пользовательское имя для накопителя


Что же, всё обещанное сделано. В следующей части я расскажу об остальных программах, которые так или иначе помогают в программировании. Жду пожеланий и вопросов.

← →

Fingercomp

Fingercomp

×