...
...

Размышления об информационной системе

Большие шкафы с аппаратурой, свисающие отовсюду провода, тысячи ламп — таким был первый компьютер. Нет... Ребята, которые его изобрели, вряд ли могли себе представить, что в будущем все это уместится на мизинце и будет работать куда быстрее. Их ЭНИАКи и МАРКи вряд ли могли с достаточной скоростью сложить два числа, не то что предоставить пользователю интерактивные возможности.

Как всегда говорится, времена меняются, и сейчас мы с легкостью слушаем музыку, смотрим фильмы, не испытывая какого-либо дискомфорта. Однако на этом никто не собирался останавливаться: Microsoft выпускает Windows Media Center Edition, Intel вовсю создает рабочие модели "цифрового дома". Но возможности, предоставляемые этими технологиями, все равно как-то не доросли до того, что мы представляем себе в будущем. Все это лишь довольно ограниченные по своим возможностям попытки добавить пользователю удобства, что касается в особенности Windows Media Center. В реальности это лишь модификация операционной системы, содержащая чуть-чуть больше драйверов, немного больше красивого интерфейса плюс возможности, которые не отличаются какими-то кардинальными изменениями. То есть, если мы вдруг присоединим к компьютеру проектор, телевизор, подключим компьютер к высокоскоростному постоянному соединению с Интернетом, то нам все равно придется идти, выбирать какие-то интересующие нас файлы, настраивать систему, работать непосредственно с компонентами операционной системы — в общем, выполнять рутинные действия. Нижеприведенная идея современной информационной системы не оригинальна и имеет довольно много общего с WinFS, но включает в себя то, что можно сделать прямо сейчас. Год-два назад нашел в Интернете книгу Билла Гейтса "Дорога в будущее". В ней даются довольно оптимистичные прогнозы насчет того, какой будет информационная система (в книге "Информационная магистраль") будущего. Основанная на идее Интернета, она должна предоставлять просто сногшибательные возможности по поиску информации, причем поиск ведется по строке вполне "человеческого" обдуманного текста. Информация должна быть доступна любому и из любой точки планеты в любом виде. Города могут представляться в трехмерном виде, выставки рисунков в виде трехмерных павильонов, куда сам виртуальный посетитель может поместить свое творение. Книга отражает еще более оптимистичные предсказания, потому неудивительно, что сам автор недавно констатировал, что "Дорога в будущее" была чересчур амбициозна. В год ее выпуска — 1995 — это, наверно, казалось еще более неправдоподобным, так как только-только вышла Windows 95, о высокоскоростном соединении почти никто, а тем более в наших широтах, не смел и мечтать. Однако более чем через 10 лет цифровые технологии взметнулись вверх, предоставляя программистам все новые и новые вершины для покорения. Настало время взглянуть на амбициозные идеи немного по-другому. Представьте, что у вас на стене висит 22-дюймовый жидкокристаллический монитор, на котором вы видите всю информацию, какую только захотите: видео, страницы Интернета, картины всевозможных художников и т.д. Ответьте на вопрос, как должна эта информация предоставляться, что это должная быть за информация, с какой скоростью должен идти поиск информации и как должен быть организован сам поиск, и можно считать, что вы уже представили, какой должна быть информационная система будущего. Например, я себе представляю это так, что я могу скачать из Сети любой документ широко известного формата (или принести откуда-то на носителе), система должна разложить его по составляющим. Для удобства я могу дать осмысленное название этому документу, добавить ключевые слова для удобства поиска, задать ему всевозможные атрибуты. А если захочу, могу попросить систему переконвертировать этот файл в другой формат. В общем, все это мечты. И их вполне реально реализовать.

Из языков для разработки системы я склонен выбрать C#. Другим, может быть, легче писать на J#, Visual Basic или вообще на Java — ничего не имею против, но свои мысли я буду излагать, основываясь именно на технологии .NET. Если писать такую сложную систему на чем-то более приземленном типа C++, то можно получить прирост в быстродействии (и то этого может и не быть), но писать саму систему будет очень сложно. А так как мы всего лишь делаем прототип, то программировать должны с удовольствием. Две самые важные вещи, которые присутствуют в .NET — это Remoting и Reflection. И вы вскоре поймете, почему. Для читателей, которые хотят лишь ознакомиться с механизмами этой системы, программистские размышления можно не читать. Хочу сразу заметить, что здесь вы не найдете полной реализации в коде. Эта статья в подавляющей своей части — всего лишь теоретическое описание. Все, что у меня сейчас есть, сравнимо с самолетом-истребителем, у которого крылья наспех прикручены одним болтом: разваливается не взлетая.

Основа системы

Из-за выбора языка программирования систему можно будет развернуть только на Windows 2000 и выше (Mono для Linux во внимание не берем, так как эта программа не представляет всех возможностей, которые даст нам родной .NET Framework). За основу можно взять обычный сервис Windows. Этот метод наиболее удобен, так как теоретически сервисы наиболее защищены от нападений недружелюбных программ и, к тому же, имеют привилегии в скорости в серверных системах.

Реализация: Сервис может создать канал, по которому будет доступен непосредственно класс информационной системы. Естественно, класс должен быть единственным для всех приложений, потому его тип регистрируется с параметром mode, равным WellKnownObjectMode.Singleton. В журнале RSDN есть статья, в которой говорится, что в регистрируемом типе лучше переопределить метод InitializeLifetimeService с возвращением из него null для того, чтобы предотвратить уничтожение экземпляра класса при длительном отсутствии обращения к нему. Если вы планируете развернуть свою систему на сервере, то можно создать для этих целей веб-сервис. Это будет более чем удобно при общении клиентов с большим архивом информации через Интернет.

Основные элементы данных

Если в обычной файловой системе типа FAT может быть только три типа элементов — файлы, папки и ссылки на файлы и папки, то новая система хранения данных должна быть более разнообразна. Естественно, все три предыдущих типа элементов должны сохраниться, но, кроме того, должны добавиться и новые. Сначала рассмотрим только некоторые из них, а по мере объяснения концепции введем дополнительные элементы. Итак, во-первых, хотелось бы вывести такое понятие, как массив, на уровень системы хранения данных. Предположим, у вас есть стандартное хранилище, куда вы хотели бы складывать информацию о своих знакомых, а именно имя, номер телефона, адрес, e-mail и тому подобные сведения. Заводить обычный текстовый файл было бы как-то неправильно, тем более, что, кроме текстового редактора, никакая программа не сможет выдать вам полезную информацию из этого файла, так как там предположительно уже будут написаны слова "Имя", "Адрес" и т.д. Вместо этого мы создаем в системе хранения шаблон элементов, на основе которого создаем массив. При каждом случае, если вам надо добавить информацию о новом человеке, вы просто создаете массив элементов. Каждый элемент может быть доступен по адресу типа "/addressbook/storage[5]", где storage и есть наш массив, а 5 — номер элемента массива (кстати, можно задействовать элемент массива номер 0 для шаблона — тогда будет очень удобно его изменять при надобности). Но есть и более элегантный выход — сделать именование элементов массива текстовой строкой, как то дает нам делать, например, C# (кто знает — тот поймет, как это удобно). Тогда вместо предыдущего варианта мы можем просмотреть информацию, воспользовавшись следующим адресом: "/addressbook/storage["Иванов И.И."]". И реализовать такую возможность очень просто, особенно если воспользоваться библиотекой из .NET Framework для работы с XML. Во-вторых, если мы уже метим на максимальный комфорт в работе с информационной системой, то вполне логично было бы ввести такое понятие, как стандартизация данных. Требуется ввести какое-то количество стандартных типов (например, целочисленный, строковый, бинарный и т.д.). Это позволит программам, которые будут работать с информационной системой, максимально удобно работать с данными, так как контроль соответствия данных с типом поля будет вестись на уровне самой системы.

Провайдеры данных

Информация в системе данных должна иметь древовидную структуру типа той, с которой вы имеете дело каждый день, открывая любой файл, однако это не совсем обычная, точнее, не совсем статичная, структура. Одна из идей системы состоит в том, чтобы предоставить пользователю доступ ко всем видам источников информации, будь то страницы сети Интернет, данные на жестком или съемном диске или вообще сервер, который может дать сведения о погоде. Все это можно назвать провайдерами данных, или просто DataProviders. Провайдером данных является обычная DLL-библиотека, созданная, конечно же, для платформы .NET. Любой (заметьте: абсолютно любой!) провайдер данных может служить основным (или, правильнее будет сказать, ведущим) провайдером, предоставляемые данные которого будут лежать в корне всей структуры. Это позволяет сделать основным хранилицем данных XML- файл, а если пользователь захочет, то может с легкостью сменить основной провайдер на провайдер, который хранит информацию в базе данных Microsoft SQL Server, что будет куда более удобно для организаций. Если же, например, база данных не является основным хранилищем, но к ней нужно довольно часто обращаться, то есть простой выход — сделать провайдер для баз данных частью структуры информационной системы. К тому же, можно сделать так, что провайдеру при считывании информации будут передаваться какие-то определенные параметры. Рассмотрим простой пример. В структуре у нас есть ссылка на провайдер, который предоставляет доступ к базе данных. В любой системе баз данных, будь то Microsoft SQL Server, MySQL или какой нибудь BDE, нужно указывать имя базы данных, имя пользователя и его пароль. Потому при создании ссылки на провайдер данных он потребует три строковых параметра. Работник компании, который не заботится о своем рабочем месте и не прочь с него вылететь, укажет сразу все данные. Нормальный пользователь сделает лучше: он передаст информационной системе два первых параметра, но поле ввода пароля оставит пустым. Если мы используем, предположим, провайдер данных на основе XML в качестве основного, то он запишет в описатель ссылки на провайдер баз данных следующие данные:

<param>
<dbname>имя_БД</dbname>
<uname>пользователь</uname>
<pwd u></pwd>
</param>

То есть в приведенном коде видно, что атрибут 'u' позволяет дать понять информационной системе, что данный параметр не определен, и требуется вмешательство пользователя.

А давайте рассмотрим еще более интересный пример. Предположим, что вам необходимо ежедневно собирать биржевые сводки с разных сайтов, на которых, к счастью, есть что-нибудь типа RSS, и распечатывать их. К тому же, у нас есть провайдер данных, который отображает эти сводки с какого- то определенного сайта, причем сводки оформлены в виде текстовых данных, имена которых являются названиями компаний, курс акций которых мы хотим узнать, и эти данные имеют вид обычных файлов, то есть их тоже можно открыть и просмотреть, и выглядеть в структуре они будут как отдельный элемент для каждой компании. Имя сайта указывается в параметрах провайдера. Как известно, документы, в том числе и текстовые, имеют свой строго определенный формат. Тогда делаем очень просто. Создаем отдельный каталог, где создаем ссылки на вышеприведенный провайдер данных, причем в каждой ссылке указываем имя интересующего нас сайта. А потом делаем следующую вещь: создаем новый каталог, а в него помещаем ссылки на данные из провайдеров. Проще будет рассмотреть на примере того же провайдера на основе XML. Создадим все интересующие нас ссылки на провайдеры в каталоге по пути "/providers/". После создания каталога непосредственно со ссылками на созданные провайдеры у нас появится следующая запись на XML:
<cat name="Prices"></cat>

После создания ссылок мы получим такую XML-структуру:

<cat name="Prices">
<link name="Microsoft" dest="/providers/prov1/microsoft">
<link name="Yahoo" dest="/providers/prov2/yahoo">
<link name="Google" dest="/providers/prov2/google">
</cat>

Как можно догадаться, здесь мы создали ссылки на первый экземпляр провайдера для того, чтобы узнать курс акций Microsoft, и две ссылки на второй экземпляр провайдера, для того, чтобы узнать курс акций Yahoo и Google с другого сайта. Теперь завершим нашу задачу. Можно написать такую программу, которая запрашивала бы значения всех текстовых элементов в каталоге, а затем выводила их на печать. Когда программа запрашивает значение, информационная система обращается к провайдеру, чтобы он выдал информацию сначала с одного сайта, а затем с другого. Тем самым некогда рутинная работа превратилась буквально в дело одного щелчка мыши. Эту же задачу можно решить немного проще, но об этом чуть позже.

Реализация: Вот тут-то нам реально понадобился Reflection. Эта технология предоставляет просто блестящие возможности для создания экземпляров классов на основе типов, определенных в библиотеках. Для провайдеров требуется определить отдельный интерфейс (например, IDataProvider), который будет доступен как самому провайдеру (что вполне естественно, так как надо сделать класс провайдера по данному интерфейсу), так и информационной системе. Для этой цели будет лучше всего создать отдельную библиотеку, где можно было бы определять такие общие для компонент системы и приложений вещи. Для диалога между программой и информационной системой и между системой и провайдерами я бы порекомендовал использовать методы с одинаковым набором параметров (просто так будет гораздо удобнее), но с разными возвращаемыми параметрами. Если при общении программы с информационной системой может вернуться все что угодно вплоть до сообщения об ошибке, то между системой и провайдером все должно быть строго стандартизировано. Потому для возврата данных к программе я могу порекомендовать использовать массив из объектов (то есть object[]), а для внутрисистемного общения завести отдельную структуру и тоже поместить ее в общую библиотеку. В системе должно быть обязательно учтено, что провайдер может вернуть запрос на переход к другому провайдеру. Так как загрузка библиотеки провайдера и создание для него экземпляра — дело довольно жадное на время, возможно, лучше создать кэш из провайдеров. Для отброса ненужных экземпляров у себя я выбрал бородатый метод, который описан в книге Э. Таненбаума "Современные операционные системы" и выполняет, как то ни странно, аналогичные функции, но со страницами памяти. Опишу его вкратце. Создаете массив со значениями int размером, аналогичным размеру кэша. Всем элементам присваиваете значение -1. Если есть потребность загрузить провайдер, достаточно лишь найти элемент массива со значением -1 и создать на соответствующем элементе массива экземпляр класса, присвоив после этого соответствующему элементу числового массива значение 0. При каждом обращении к тому или иному провайдеру просто увеличивайте значение соответствующего счетчика на 1. Если же при загрузке пустого элемента не оказалось, то просто удаляем экземпляр провайдера, у которого значение счетчика самое маленькое, а на его месте создаем экземпляр запрашиваемого класса, после чего, опять же, присваиваем соответствующему счетчику значение 0.

Преобразование данных

Довольно часто в подобных проектах встречаются идеи по поводу преобразования и разбиения данных на осмысленную структуру — например, музыкальной композиции на элементы, которые несут в себе по отдельности сведения о длительности композиции, ее имени, авторе, и отдельно — непосредственно аудиопоток. В терминологии WinFS это называется схемами данных, в небезызвестном всем ОСеписателям проекте 3OS[1] — кодеками. Идея состоит в том, чтобы, имея некие данные об определенном формате, разбивать входящий файл или поток на определенную структуру, описывающую этот файл. Это должно существенно упростить и ускорить процесс поиска данных. Кодеки, на мой взгляд, лучше всего реализовать через те же самые библиотеки. Подумайте сами: если мы будем использовать именно библиотеки для .NET, то нам откроются возможности по наследованию кодеков, что позволит реализовать обработку производных типов. Но это относится скорее к проблеме реализации, чем к теории. Процесс анализа входящего файла должен начинаться с анализа так называемой сигнатуры файла — нескольких символов или кодов в самом начале файла, которые уникальны для каждого формата файла. Разумеется, есть и форматы, в которых таких особенностей нет — это, например, обычные текстовые файлы, равно как есть вероятность, что файл будет распознан неправильно (мало ли что попалось во входном потоке). Потому было бы разумно дать пользователю возможность выбрать между автоматическим присваиванием кодека и присваиванием только по команде пользователя. Со стороны пользователя структура, выдаваемая кодеком, не должна отличаться от обычной информации в структуре данных. Например, если программа хочет прочитать весь музыкальный файл в формате MP3, лежащий по пути "/myfiles/musicfile", то она должна послать запрос именно с именем этого файла. Если же программа хочет прочитать только название композиции, то она уже посылает запрос на получение данных по пути "/myfiles/musicfile/name". Инфомационная система должна сама понять, что хочет программа, и перенаправить запрос куда надо.

Можно рассмотреть несколько более интересный пример. Пусть у нас есть видеофайл в формате DVD, лежащий по пути "/myfiles/myvideo". Предположим, что пользователю не нужен такой хороший формат, и он готов ограничиться чем-то вроде DivX-формата. При наличии соответствующих возможностей кодека мы присоединяем его к этому файлу и посылаем команду на чтение "/myfiles/myvideo/divx", после чего программа получает готовый файл в формате DivX. Рассмотрим еще один пример использования кодека (надеюсь, дорогие друзья, я вас еще не утомил:)). Предположим, что у нас есть звуковой файл с очень высоким битрейтом (например, 192 Кбит/с), располагающийся по пути "/myfiles/musicfile". Естественно, нам такой хороший файл не нужен, и мы хотим файл с битрейтом 128 Кбит/с. Присоединяем к этому файлу соответствующий кодек и отправляем запрос на данные "/myfiles/musicfile/bitrate/128". Кодек должен сам преобразовать файл в более скромный и отослать его клиенту. Тем самым не обязательно выбирать какие-то фиксированные возможности, так как можно было бы с таким же успехом указать битрейт и 64, и 32 Кбит/с, а понять это — уже задача кодека.

Реализация: думаю, вы уже заметили, что кодеки чем-то напоминают все те же провайдеры данных. Да, и те, и другие реализуют часть структуры всей информационной системы, потому тут все повторяется: все тот же Reflection, то же создание экземпляров классов и, в общем-то, те же механизмы работы исключая то, что кодеки лучше не кэшировать, так как, в отличие от провайдеров, их должно быть намного больше, потому меняться они будут очень часто, что создаст только неудобства и дополнительные затраты времени. Но тут есть одно отличие от провайдеров: при подаче запроса кодеку ядро информационной системы должно передать кодеку и ссылку на содержимое файла, к которому относится этот кодек. Интерфейсы провайдеров и кодеков должны быть максимально схожи для удобства в разработке ядра информационной системы.

Классы

Мы подошли к одной из ключевых тем данной идеи. Классам можно дать довольно схожие определения, но наиболее точным я считаю описание этого термина, данное Гербертом Шалдтом в его книге "Полный справочник по C#". Не вдаваясь в сложную терминологию, можно определить класс как единицу, которая включает в себя как данные, так и код, который будет оперировать этими данными. Экземпляры этих классов называются объектами, или экземплярами класса. С точки зрения информационной системы класс представляет собой все ту же DLL-библиотеку, в которой реализован настоящий класс, который может оперировать данными. Для более подробного рассмотрения проблемы классов давайте вернемся к примеру с распечаткой курса акций. Предположим, что у нас есть класс, в котором реализована процедура PrintPrices, которая печатает котировки. Для того, чтобы узнать курс акций, этой процедуре нужно всего лишь XML-дерево, в котором будут указаны имя компании и курс акций. Мы делаем те же действия, какие описаны в разделе про провайдеры данных, но вместо создания обычного каталога со сводками создадим экземпляр класса. Его создание отличается от создания каталога только тем, что класс создает внутри своего экземпляра еще какую-то структуру, состоящую из каталогов и данных, которая нужна ему для нормальной работы. И есть еще одна важная особенность: теперь мы можем выполнять процедуры, которые реализованы в классе. Теперь, когда мы записали все ссылки на провайдеры в наш класс, мы можем послать информационной системе следующий запрос: "/prices.PrintPrices()". Система должна переслать этот запрос непосредственно DLL-библиотеке, в которой реализован данный класс, после чего процедура PrintPrices выполнится, и мы получим долгожданные курсы на акции. Удобство заключается также и в том, что мы можем с легкостью добавлять и удалять ссылки на провайдеры данных, потому распечатана будет только нужная нам информация. В процессе обдумывания темы классов я вспомнил о такой штуке, как Avalon. Для тех, кто не знает: Avalon — это система отображения видеоинформации включая окна приложений, разрабатываемая Microsoft. Одним из ее свойств является то, что положение элементов типа кнопок, полей ввода и т.д. описывается на производном от XML языке XAML[2]. Вот пример описания обычной кнопки на XAML:

<Button Name="Button1" Click="Button1_OnClick" Width="300" Height="100" Content="Кнопка1"/>

При желании таким же методом можно воспользоваться и в информационной системе. Если программист предоставляет пользователю возможность модифицировать интерфейс программы, то он может реализовать его описание в определенной структуре, а саму программу реализовать в виде класса. Тогда ее можно будет вызвать запросом типа "/myprogram.Execute()". Из этого вытекает еще одно следствие: настройки программы могут храниться прямо вместе с ней, так что при переносе программы с одного компьютера на другой не надо будет заново ее настраивать.

Реализация: для классов информационной системы следует завести отдельный интерфейс и делегат для методов, которые можно будет исполнять, подав соответствующий запрос информационной системе. В делегате должно быть учтено два параметра. Первый — это строковый параметр, в котором будет передаваться путь к классу в структуре. Есть другой вариант — передать классу ссылку на XmlNode, в котором будет находиться содержимое той ветки структуры, которая входит в данный экземпляр класса. Этот вариант будет более преемлемым. Вторым параметром должен быть массив, в котором будут содержаться параметры вызова метода.

Структуры

Что вы будете делать, если захотите описать какой-то документ стандартного формата? Например, у вас есть аудиофайл типа wav, но вы хотите сделать его полное описание в структуре, добавив информацию об авторе, альбоме, годе выпуска и прочее. Вы просто создадите новый каталог с именем композиции, запишете в него отдельно сам аудиофайл, во второй блок в этом же каталоге запишете название альбома, в третий блок — длительность композиции — ну, и так далее. А что, если таких файлов 10, 50 или 100, и все должны быть описаны подобным образом? Ответ прост: создайте структуру. Под структурой мы будем понимать именованный прописанный в информационной системе "шаблонный" каталог, в котором будут содержаться конструкции, характерные для того или иного формата. Например, обычный исполняемый exe-файл можно разбить на определенную структуру: заголовок, информация о положении таблиц импорта и экспорта, информация о начале непосредственно кода программы и т.д. Структуры будут очень полезны при задании программами определенного формата для своих файлов, так что любая другая программа при желании может выудить нужную информацию из этой структуры, тогда как для обычных файлов — таких, как, например, файлы doc, exe, zip, — нужно искать документацию, а потом уже реализовывать поиск нужной информации.

Реализация: структуры можно представлять как отдельные XML-файлы, которые описывают всю структуру. Если мы планируем сделать форматы уникальными, то надо учесть механизм регистрации и удаления структур из информационной системы.

Транзакции

Транзакции представляют собой попытки записи какой-то информации в базу данных или в другое хранилище с учетом того, что, если попытка записи не удалась из-за сбоя в аппаратном или программном обеспечении, то не будет записана никакая информация, которая была изменена в тот период, пока транзакция была открыта. Такую возможность обязательно должна содержать и информационная система будущего. Ее реализация не так сложна. Если мы открываем транзакцию, то создается XML-файл, в котором отражаются абсолютно все изменения в хранилище. Например, если транзакция установлена на уровне каталога "/folder/one", а мы хотим записать информацию в каталог "/folder/one/supfolder", то до supfolder запрос просто не доходит, а только отражается в XML-файле, который описывает данную транзакцию. После завершения транзакции информационная система начинает завершать запросы начиная с самых глубоких запросов, попавших в транзакцию, и тут... отключается электричество. В принципе, ничего страшного. После загрузки ОС и информационной системы последняя сперва начинает проверку, не осталось ли незавершенных транзакций. Естественно, если система их обнаружила, то она начинает их завершать, после чего помечает транзакцию как завершенную. Если же в процессе отключения электричества был поврежден файл, описывающий транзакцию, или даже главный файл, в котором содержится вся информация информационной системы (и копию этого файла мы, конечно же, не сделали), то, увы, транзакцию или файл уже ничто не спасет. Я думаю, было бы логично на время завершения транзакций отклонять запросы на чтение/запись данных в тот каталог, куда система записывает измененные в течение транзакции данные.

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

Заключение

Мне кажется, если объединить все мною описанные технологии в один продукт, то можно получить настоящую систему для управления данными, с помощью которой можно приблизить такое понятие, как "технологии XXI века". Конечно, многое не учтено: это лишь мое скромное представлении об информационной системе будущего. Можно к этому делу прикрутить что-то типа языка запросов в роде SQL, но это будет довольно сложно. Если же все- таки решитесь реализовать что-то типа этого языка, то настоятельно рекомендую ознакомиться с языком XQuery[3]. Его уже используют при разработке баз данных, так что, думаю, он подойдет и нам. Разумеется, реализовать все мною описанное будет довольно или даже очень сложно, тем более если учитывать все нюансы, но реализация хотя бы части этой идеи сойдет по крайней мере для курсовой работы:).

Если кому-то будет интересно взглянуть на нынешние мои наработки — пишите на e-mail. Буду очень рад прочитать ваши комментарии, вопросы, предложения и сообщения, стоит ли вообще продолжать эту тему на страницах КГ. При наличии интереса со стороны читателей я могу описать все более подробно. А на сегодня позвольте откланяться. Всего доброго.

-----------------------
[1] сайт
[2] сайт
[3] сайт

Влад "Dreamer" Маслаков, dreamer.mas@gmail.com

© Компьютерная газета

полезные ссылки
IP камеры видеонаблюдения