ET-prolog - делаем телефон умнее

В последнее время "умным" стало большинство приборов, с которыми нам приходится работать. Но что стоит за этим словом? Ум (пока) не свойственен технике, поэтому мы говорим об имитации интеллекта и мерилом качества имитации являются следующие правила:
  • Прибор должен делать то, что от него хочет пользователь
  • Если прибор делает не так, как велел пользователь, то он должен аргументировать свое поведение, и пользователь должен согласиться, что был не прав. Контроль ошибок пользователя
  • Прибор должен по-возможности угадывать, что от него хочет пользователь
И вот с последним пунктом возникают основные проблемы. Массовый прибор, такой как мобильный телефон, не может учитывать индивидуальность всех своих пользователей. Это значит, что он может угадывать только наиболее общие желания. Существует два способа сделать устройство умнее:
  • За вас уже подумали лучшие специалисты: психологи, программисты, дизайнеры, экономисты. Ваш девайс эргономичен и удобен. Зачем вам то, что вы хотите? Это не модно. Расслабьтесь и наслаждайтесь. Другими словами: "Ешь, что дают, и привыкай".
  • Дать возможность настроить устройство под себя. Не темы оформления, а само поведение устройства. Как правило, реализуется в виде кучи галочек и кнопочек в настройках программы.
    • Нас интересует только второй вариант. В рамках отдельных программ он существовал достаточно давно, сейчас пришло время, когда программы плотно взаимодействовать друг с другом. Сейчас есть удобные распространенные и унифицированные технологии, позволяющие программам объявлять во всеуслышание что они делают и управляться из других программ. И, если пройтись по репозиториям для n900 можно найти немало пакетов, подстраивающих работу других программ под пожелания конкретного пользователя.
      Начать, наверное, стоит с классического cron, выполнения команд в соответствии с раписанием:
      • Fcron - классический cron, управляется через конфигурационный файл
      • alarm - то же самое, только с менее удобным, но более понятным графическим интерфейсом
      • silencer умеет только переключать режимы телефона. Зато интерфейс совсем простой.
      • sleeper умеет выключать музыку через заданное время.
      Но ведь не обязательно реагировать на таймер, в системе происходит куча других событий. Ваш телефон пищит, когда садится батарейка, регулирует яркость экрана в зависимости от освещения, ноутбуки регулируют соотношение производительность/энергосбережение в зависимости от наличия питания. Есть в репозитариях и ряд пакетов, использующих этот подход:
      • headphone daemon ставит на паузу воспроизведение музыки при вынимании штекера наушников. Прекрасный пример, когда нет единого решения для всех. Разработчики maemo на просьбу добавить этот функционал в прошивку ответили, что раньше так и было, на народ просил, чтобы ничего на паузу не ставилось.
      • lockdaemon пикает и вибрирует при выдвигании клавиатуры. Терпеть не могу озвучивание управления, а кому-то нравится
      • shake2control - управляет воспроизведением в зависимости от того, как вы трясете телефон.
      • geotodo - показывает напоминалки, когда пользователь находится рядом с указанным местом.
      Но есть у всех этих программ одна общая черта: те события, на которые они реагируют и действия, которые предпринимают, зашиты на этапе программирования. Представьте, сколько разных событий может возникать и сколько разных действий можно совершить, умножьте эти числа и получите количество программ, которые нужно написать. Первое, что приходит в голову - отслеживать наступление событий и дать пользователю самому определять, что нужно делать при каждом из них. Получается всего одна программа. И по такому пути пошли разработчики ActionManager и Do What I Mean Daemon.
      Насколько мне известно, это вершина технологий, используемых для увеличения "интеллекта" приборов. И основное её ограничение - простота используемых правил. При выборе действия трудно учитывать несклько факторов одновременно. Автор silencer предлагает включать тихий режим ночью, но спать то я ложусь в разное время. Более корректно было бы включать его с 10 до 2х когда вокруг стало темно и тихо, а выключать по будильнику или 10 утра.
      Соответственно появилась идея создать язык программирования, на котором можно было бы кратко и легко, не вникая в особенности межпроцессной коммуникации, написать аналог headphone daemon. Чтобы он уместился в несколько строк кода, а не в 272:
      меня интересует состояние гнезда наушников
      если плеер что-то играет, наушники не воткнуты, а до этого были воткнуты, то остановить плеер
      Продвинутый пользователь сможет легко описать на таком языке желаемое поведение телефона, конкретно под свои нужды. Неопытный... либо скопирует готовые скрипты из интернета, либо воспользуется графической программой, генерирующей частые случае таких скриптов. Написать такую программу значительно проще, чем демона, отслеживающего состояние системы.
      Желаемый язык очень похож на логическую парадигму программирования, поэтому в качестве базового языка был выбран SWI-Prolog и к нему было написано расширение для работы с событиями и темпоральной логикой (ведь нам надо оперировать такими понятиями, как "в предыдущий момент времени", "последние 15 минут", "когда-то случилось"). В результате получался управляемый событиями темпоральный пролог или Event driven Temporal prolog - ET-prolog. Аналог headphoned на нем будет выглядеть примерно так:
      init.pl
      ?- addGuard(headphonesState(_), [[changedGuard]], 'logic/headphones.pl').
      headphones.pl:
      ?- headphonesState('disconnected\n'),previous(headphonesState('connected\n')),
      (mediaPlayer('started'),pauseMediaPlayer;true),
      (MPlayer('started'),pauseMPlayer;true),
      (Panucci('started'),pausePanucci;true),
      (FMradio('started'),pauseFMRadio;true)
      ;true
      В настоящий момент предикаты управления плеерами еще не реализованы, но, во-первых, их надо будет написать всего один раз, а во вторых, если они управляются через dbus, это будет не сложно. Например предикат переключения режимов телефона занимает всего одну строку:
      setPhoneProfile(PROFILE) :- dbusSend('com.nokia.profiled', '/com/nokia/profiled', 'com.nokia.profiled', 'set_profile', [PROFILE]).
      А более полноценная версия, отслеживающая текущий профиль и работающая, как полноценный прологовский терм, в обе стороны, выглядит так:
      % Initializes profile term
      ?- 
      	dbusSend(CURRENTPROFILE, 'com.nokia.profiled', '/com/nokia/profiled', 'com.nokia.profiled', 'get_profile', [], false),
      	newTemporalTerm('phone profile', [true,true,CURRENTPROFILE,false]), 
      	addDBusListener('signal', '/com/nokia/profiled', 'com.nokia.profiled', 'profile_changed', false, false, temporalTerm(_, 'phone profile')).
      % silent or general
      phoneProfile(PROFILE) :- string_to_atom(PROFILESTR, PROFILE), temporalTerm([_,_,PROFILESTR | _], 'phone profile');  dbusSend('com.nokia.profiled', '/com/nokia/profiled', 'com.nokia.profiled', 'set_profile', [PROFILESTR], false).
      Если этот проект вас заинтересовал, то вы можете: ET-Prolog разработан Dicentra13 - Петрушкиной Анастасией (интеграция с alarmd, liblocation, файловые термы) и KiberGus - Гусейновым Алексеем (ядро языка, интеграция с DBus)