Вверх ↑
Главный модератор
Ответов: 2999
Рейтинг: 396
#1: 2019-10-14 19:08:03 ЛС | профиль | цитата
Эта тема создана для возможных разработчиков элементов проекта HiAsm.NET. Это предполагает, что Вы разработчик программного обеспечения на каком-либо языке и не обязательно высокого уровня, а также уже создавали программы в HiAsm какой-либо версии. Очень помог бы Ваш опыт разработки элементов для других пакетов, но это также не обязательно. Для разработки полноценного элемента понадобится инструмент в виде программы Visual Studio, начиная с версии 2010 года. Если у Вас возникли трудности с установкой, то поищите в интернете информацию по этому вопросу. Например, вот - первая обнаруженная ссылка на данную тему на не очень русском языке, но вполне "понимабельная".

  Предположим, что Вы установили инструмент разработки, собственно конструктор программ HiAsm.NET и готовы создать новый элемент для пакета Core. Следуя традиции, сначала элемент будет очень простым, но постепенно будем добавлять в него функционал до максимально возможного уровня, достигнутого на сегодня.

Создание проекта нового элемента

  Для создания проекта нового элемента надо правильно выбрать его тип:

Внимание! Важные пункты выбора типа помечены жёлтым маркером на картинке.
Примерно вот так будет выглядеть экран, если Вы установите VS2017, выберете темную тему и английский язык:

Добавление ссылок на другие сборки

  Чтобы «разговаривать» с конструктором на одном языке, элементу надо указать где находятся две сборки конструктора, в которых хранится код классов общения со средой. Воспользуемся панелью обозревателя решения (Solution Explorer) и укажем на файлы конструктора MSDK.dll и ProxyDomain.dll в папке C:\HiAsm.NET\:





Теперь Visual Studio будет нам подсказывать на какие буковки на клавиатуре давить пальцем . Эта функциональность называется IntelliSense. Для сборок конструктора устанавливаем свойство CopyLocal значением False, чтобы в момент компиляции Visual Studio не копировала эти файлы никуда.

В заключении наведём порядок среди ссылок на сборки при помощи контекстного меню, вызываемого правой кнопкой мыши на самих ссылках, чтобы удалить ненужные нам сейчас и напротив добавить одну нужную системную сборку System.Drawing.dll:

Вот так окончательно должен выглядеть почищенный список:

Для эстетической завершенности переименуем класс из бездушного названия в понятное только нам :


Bingo
Установка атрибутов

  Теперь надо установить обязательные атрибуты, чтобы проект HiAsm.NET признал элемент и прочитал из него необходимую информацию для взаимодействия с ним. Прежде всего это жестко определённое пространство имён (namespace):
using HiAsm;
...
namespace ElementVirtual
{
...
}
Если пространство имён будет названо по-другому среда не признает сборку как контейнер элемента пакета. Отмечу, что сборкой будем называть, полученный после компиляции проекта файл с расширением .dll, который будет содержать в себе код одного или нескольких элементов.

Информационный атрибут с почтой автора и версией элемента:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
...
}
Атрибут с информацией об элементе:
...
namespace ElementVirtual
{
...
[Type("Example", "Brief description", tab = "Core")]
...
}
Этим атрибутом задаётся имя класса, краткое описание и группа в панели инструментов.

Теперь, после выбора имени класса элемента, зададим атрибут с информацией об иконке элемента:
...
namespace ElementVirtual
{
...
[ToolboxBitmap(typeof(Example), "Resources.picture.ico")]
...
}
Позднее добавим иконку с именем picture.ico в ресурсы сборки.

Определим элементу свойтво с именем Greeting:
...
namespace ElementVirtual
{
...
[Property("Greeting", "The greeting message", DataType.data_str)]
...
}
Кроме имени здесь определяем краткое описание свойства и его тип. Про тип пока отметим, что это строка текста. Полный список типов будет позже.

Определим элементу левую точку с именем doSayHello:
...
namespace ElementVirtual
{
...
[Point("doSayHello", "Do greeting message", DataType.data_str, PointType.pt_work)]
...
}
Слева направо задаются имя точки, краткое описание, тип данных, принимаемых точкой, место расположения точки на элементе. У точки могут быть только 4-ре места расположения на элементе:
    pt_work - левая точка;
    pt_event - правая точка;
    pt_var - нижняя точка;
    pt_data - верхняя точка;

Аналогично добавим элементу по одной точке на каждую сторону:
...
namespace ElementVirtual
{
...
[Point("onGreeting", "Returns greeting message", DataType.data_str, PointType.pt_event)]
[Point("Greeting", "Returns greeting message", DataType.data_str, PointType.pt_var)]
[Point("Data", "Defines greeting message", DataType.data_str, PointType.pt_data)]
...
}

Посмотрим как это выглядит суммарно:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
[Type("Example", "Brief description", tab = "Core")]
[ToolboxBitmap(typeof(Example), "Resources.picture.ico")]
[Property("Greeting", "The greeting message", DataType.data_str)]
[Point("doSayHello", "Do greeting message", DataType.data_str, PointType.pt_work)]
[Point("onGreeting", "Returns greeting message", DataType.data_str, PointType.pt_event)]
[Point("Greeting", "Returns greeting message", DataType.data_str, PointType.pt_var)]
[Point("Data", "Defines greeting message", DataType.data_str, PointType.pt_data)]
...
}
С помощью атрибутов мы сообщили достаточно информации об элементе для его взаимодействия со средой конструктора. Можно определить ещё больше подробностей, но для простого элемента этого набора достаточно.
Создание кода основного класса элемента

  Простые элементы являются наследниками базового класса Element:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
...
public class Example : Element
{
...
}
...
}

Добавим конструктор, который будет вызван средой во время создания экземпляра класса элемента:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
...
public class Example : Element
{
public Example(PackElement pe, SDK sdk, int x, int y) : base(pe, sdk, x, y)
{
flag |= (int)(ElementFlag.IS_SYSTEM | ElementFlag.IS_VIRTUAL);
}
...
}
...
}
Пока скажем только о флагах элемента: IS_VIRTUAL - сообщает среде, что класс загружен из внешней сборки; IS_SYSTEM - разрешает использование элемента для построения программ (другими словами этот флаг отличает элемент от элементов помощников, которые работают только в редакторе конструктора).

Посмотрим что у нас получилось после всех предыдущих изменений кода:

Если мы теперь построим сборку элемента у Visual Studio не будет к нашему коду никаких претензий:

По умолчанию Visual Studio сохранила файл сборки в папке проекта. Куда именно увидим, посмотрев на свойства проекта:


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

Ещё установим некоторые важные свойства проекта:

Можем заполнить форму с подробной информацией о сборке и если будете использовать данный проект как шаблон для создания других элементов, то следите чтобы уникальный идентификатор сборки (GUID) оставался уникальным:

Теперь, если опять выполним построение (Build) проекта элемента, то обнаружим в указанной ранее папке файл сборки нашего нового элемента:

Размещение элемента на панели инструментов

  В принципе, поместив файл сборки в папку \virt пакета Core, можем использовать элемент в конструкторе, но только пока для отображения схем с его использованием. Элемент надо поместить на панель инструментов, чтобы можно было его вставлять в схемы. Сделать это можно несколькими способами. Не буду описывать их все, а предложу самый простой и быстрый способ. На мой взгляд, проще всего открыть файл сборки в конструкторе через меню Open или набросить его на окно программы. Но прежде чем это делать, надо добавить к элементу ещё один атрибут с информацией о пакетах в которые надо его установить (пакетов может быть несколько):
...
namespace ElementVirtual
{
...
[Install("_base", "Simple demo", "Core")]
...
}
Слева направо задаются имя пакета, краткое описание и группа в панели инструментов. Компилируем сборку элемента снова и можем попытаться установить элемент в конструкторе. Запускаем конструктор (попытка перекомпиляции элемента при запущенном конструкторе будет неудачной, так как он уже используется и загружен в память процесса программы) и открываем его из папки \virt:
Demo


Добавление иконки элемента в ресурсы сборки

  Чтобы добавить иконку элемента в ресурсы сборки надо сначала её создать. Надеюсь на просторах интернета Вы сможете отыскать изображение достойное разрабатываемого элемента. Требования к файлу иконки следующие: 24 пикселя или точки в ширину и высоту, 32 бита глубины цвета и формат иконки Windows. С именем файла иконки мы определились ранее при добавлении атрибута ToolboxBitmap, а именно picture.ico. Теперь надо решить как добавить файл иконки в ресурсы. Есть по крайней мере два способа известных мне (вероятно их больше). Один способ очевидный, а другой нет, но зато быстрый. Давайте посмотрим сначала на очевидный способ добавления через свойства проекта:
Demo


Дополнительно в обозревателе решения устанавливаем свойства для файла иконки:

Если теперь перекомпилим проект элемента и запустим конструктор, то увидим новую иконку на панели инструментов и можем добавить её на рабочее поле схемы:


  Мы сделали заготовку или шаблон элемента, который отображается на панели инструментов и может быть добавлен в схему пакета Core. Теперь надо научить элемент реагировать на обращения к его точкам других элементов схемы (сейчас он не реагирует никак, так как в нём нет кода, реализующего данный функционал).

Создание кода методов обработки обращений к точкам элемента

  При помощи связей (линий) к элементу можно обращаться из других элементов. Причём сам элемент обращается к другим элементам через верхние и правые точки, а к нему обращения приходят на левые и нижние точки. Значит в коде элемента мы должны реализовать два метода-обработчика обращений к левым и нижним точкам и научиться получать данные с левых и верхних точек, а также отправлять результаты своей работы на правые и нижние точки.

  Метод-обработчик обращений к левым точкам элемента выглядит так:
public override void do_work(ElementPoint point, ref TData data)
{
base.do_work(point, ref data);
...
}

  Метод-обработчик обращений к нижним точкам элемента выглядит так:
public override void read_var(ElementPoint point, ref TData data)
{
base.read_var(point, ref data);
...
}
Аргументы методов - это экземпляр объекта, вызываемой точки, и ссылка на объект данных, отправленных другим элементом этому. Прочитать данные, пришедшие на левую точку можно так:
    object x = data.value; // читаем нетипизированные данные с левой точки
но грамотным с точки зрения парадигмы схемотехники HiAsm будет другой код:
    // читаем данные как объект типа TData
TData dt = readData(data, getPointByName("Data"), getPropertyByName("Greeting"));

// или читаем данные как строку
string text = readString(data, getPointByName("Data"), getPropertyByName("Greeting"));

// или читаем данные как целое число
int number = readInteger(data, getPointByName("Data"), getPropertyByName("Greeting"));

// или читаем данные как действительное число
double real = readReal(data, getPointByName("Data"), getPropertyByName("Greeting"));

Отправить данные другому элементу через правую точку может следующий код:
    // отправляем строку другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), "Hello!");

// отправляем целое число другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), 123);

// отправляем действительное число другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), Math.PI);
Универсальным способом отправки данных через правую точку будет следующий код:
    // создаём новый объект типа TData и указываем наши данные для отправки как аргумент конструктора
TData dt = new TData("Hello!");

// отправляем данные другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), ref dt);

Для возвращения данных с нижних точек надо записать наши данные в объект типа TData, ссылку на который нам прислал, обращающийся к нашей точке элемент. Следующий код показывает как можно это сделать:
    // записываем наши данные типа TData по ссылке другого элемента
data.assign(new TData("Hello!"));

// или записываем наши данные любого типа по ссылке другого элемента
data.assignT(this);

Теперь мы готовы научить наш элемент реагировать на внешние обращения к нему из других элементов:
private TData local;

public override void do_work(ElementPoint point, ref TData data)
{
base.do_work(point, ref data);

this.local = readData(data, getPointByName("Data"), null);
TData dt = new TData(this.local);
this.on_event(getPointByName("onGreeting"), ref dt);
}

public override void read_var(ElementPoint point, ref TData data)
{
base.read_var(point, ref data);

if (this.local.isEmpty())
data.assign(new TData(Properties.Resources.greeting));
else
data.assign(this.local);
}
Добавим в ресурсы сборки строку приветствия, которая будет возвращаться с нижней точки по умолчанию:

Посмотрим на получившийся код элемента:

Можете скачать полный код проекта элемента: MyElementCore.zip

  Мы реализовали необходимый функционал элемента и можем проверить как он работает:
Demo



P.S. Продолжение следует. Эта тема будет редактироваться и дополняться новой информацией для разработчиков, которая призвана помочь им преодолеть порог вхождения. Автор будет удалять комментарии не относящиеся к данной теме, чтобы не засорять её бесполезной информацией, для которой на форуме есть специальные разделы. Спасибо за понимание.
карма: 6
Дорогу осилит идущий. Install/Update HiAsm.NET
2
Голосовали:Konst, strannik_nebes
Редактировалось 80 раз(а), последний 2020-01-02 20:07:33