Lock ON и железная кабина. — «Железные» кокпиты

Возникла необходимость (по убедительной просьбе любимого племянника) подключить к военному симулятору Lock On ГС-2 существующую железную кабину, которая работает под МФС.
Что оказалось для меня совсем не простой задачей. Тем более, что программа управления кабиной должна находиться на другом компьютере. Но надеюсь с помощью коллег по симулятору эту проблему решить.

Как известно работа с этой программой обеспечивается за счет взаимодействия по протоколу TCP.
Механизм работы примерно следующий :  программа связи симулятора со внешними устройствами создает TCP «сервер» (ведущий процесс) , который ( упрощенно ) является  областью в памяти компьютера .
Эту область программа связи симулятора с внешними устройствами постоянно прослушивает. В свою очередь  ЛокОн создает «клиент» ( то есть ведомый процесс  ) который  постоянно передает в эту область данные. Увидев наличие данных  программа связи должна считать их, и преобразовав выдать во внешние устройства.  Лок Он при запуске анализирует, создан ли сервер. Если сервер не создан, то Лок Он не будет передавать данные. Таким образом, для  корректной работы необходимо СНАЧАЛА запустить программу связи (сервер), и только после этого ЛокОн.
По совету Андрея (Lisa) попробуем применить такую схему:
Lock On => Lua => TCP/IP => Sioc => клиент => приборы.
SIOC используется исключительно как сервер на который Lock On посылает данные.
Для того , чтобы ЛокОн начал работать с созданным сервером необходимо :
— объяснить программе, что она должна работать по TCP.
— указать программе область памяти ( или сетевой адрес ) в который ей необходимо передавать данные.
— указать программе объем данных ( параметры  ), который необходимо передавать .

Ответов: 47

1 2
После настройки программы:
— сформировав сервер дождаться начала обмена данными .

Эта последовательность действий обеспечивается следующим образом.
1. В  файле  …1CEagle DinamicsLOConfigExportConfig.lua
найти строку EnableExportScript = false  ( в принципе она одна там и есть )…
и исправить её так :EnableExportScript = true
Этим мы дадим знать ЛокОн что ему необходимо начать работать на передачу (экспорт)
данных.
2. Заменитьфайл …1CEagle DinamicsLOConfigExportExport.lua
на одноименный файл , ( то есть с нашими данными )
Желательно старый файл не удалять а переименовать (на всякий случай ).

В этом файле находятся :
— настройки TCP ( номер порта и номер хоста )
— ————————————————————
—процедурыоткрытияпорта
functionLuaExportStart()  
package.path  =package.path..»;.\LuaSocket\?.lua»  
package.cpath =package.cpath..»;.\LuaSocket\?.dll»  

socket =require(«socket»)  
host =»192.168.10.190″—сетевойадресSIOC
port =8092– порт SIOC
c = socket.try(socket.connect(host, port))  
c:setoption(«tcp-nodelay»,true)  
c:settimeout(.01)  
end
————————————————————————————
—значения»host»и»port»поумолчаниюдляSIOC
host – сетевой адрес компьютера на котором установлен сервер.

Запускаем SIOC сервер, затем LockOn, выбираем миссию. Компьютеры связаны по локальной сети.
В строчке «Clientsconnecteds» должны увидеть, сколько клиентов подключены. Если соединение получилось уже хорошо.

Прикрепленные изображения

  • Sioc.JPG

Сообщение отредактировал ra3dcs: 08 September 2012 — 14:44

Теперь дальше получаемые данные полета.
— эта функция выполняется после отрисовки каждого кадра
function LuaExportAfterNextFrame()
local lSIOC_SendString = «»
local TAS = LoGetTrueAirSpeed()* 3.6 — получаем значение истинной скорости
local IAS = LoGetIndicatedAirSpeed()* 3.6 — получаем значение прибрной скорости  
local AOA = LoGetAngleOfAttack() — угол атаки
local Gp  = LoGetAccelerationUnits() — перегрузка
local VV  = LoGetVerticalVelocity()   — вертскорость
local altBar = LoGetAltitudeAboveSeaLevel()
local altRad = LoGetAltitudeAboveGroundLevel()
local pitch, bank, yaw = LoGetADIPitchBankYaw()
local Mach = LoGetMachNumber()

local HSI = LoGetControlPanel_HSI()
local engine = LoGetEngineInfo()

local route = LoGetRoute()

local PlaneID = LoGetPlayerPlaneId()
local obj = LoGetObjectById(PlaneID)

local myXCoord, myZCoord = getXYCoords(obj.LatLongAlt.Lat, obj.LatLongAlt.Long)

local navi = LoGetNavigationInfo()
R0 = TAS
R1 = IAS
R2 = AOA*ToDegree
R3 = Gp.y
R4 = VV
R5 = altBar
R6 = altRad

— округляем значение до целого
R0 = math.floor(R0 + 0.5)
R1 = math.floor(R1 + 0.5)
R2 = math.floor(R2 + 0.5)
R3 = math.floor(R3 + 0.5)
R4 = math.floor(R4 + 0.5)
R5 = math.floor(R5 + 0.5)
R6 = math.floor(R6 + 0.5)

— формируем строчку номер переменной = значение
R0 = «0=»..R0..»:»
R1 = «1=»..R1..»:»
R2 = «2=»..R2..»:»
R3 = «3=»..R3..»:»
R4 = «4=»..R4..»:»
R5 = «5=»..R5..»:»
R6 = «6=»..R6..»:»
— записываемстрочку
lSIOC_SendString = lSIOC_SendString..R0
lSIOC_SendString = lSIOC_SendString..R1
lSIOC_SendString = lSIOC_SendString..R2
lSIOC_SendString = lSIOC_SendString..R3
lSIOC_SendString = lSIOC_SendString..R4
lSIOC_SendString = lSIOC_SendString..R5
lSIOC_SendString = lSIOC_SendString..R6
— отправляем
if lSIOC_SendString ~= «» then
socket.try(c:send(«Arn.Resp:»..lSIOC_SendString..»n»))
end
end
— процедурызакрытияпорта
function LuaExportStop()
io.close()
socket.try(c:send(«quit»)) — to close the listener socket
c:close()
end

——————————————————————
Приведенный Андреем (Lisa) пример формирования строки переменной для SIOC выглядит очень громоздким. Если, к примеру, мне нужно 50 переменных понадобиться писать более 200 строк?
Наверно существует другой способ записи, может, кто знает и подскажет?

Сообщение отредактировал ra3dcs: 08 September 2012 — 21:03

ra3dcs (08 September 2012 — 21:02) писал:

Наверно существует другой способ записи, может, кто знает и подскажет?

Если этот язык программирования поддерживает массивы и циклы, то, очевидно, надо данные сложить в массив и отправить их в цикле. А приведенный вами код тоже не самый удачный — в нем все переменные складываются в одну строку и отсылаются все скопом если изменилась хотя бы одна переменная. По-хорошему надо отсылать только те переменные, которые не изменились. Описание этого языка программирования доступно? Я мог бы почитать его и что-нибудь присоветовать…

САБ (09 September 2012 — 02:17) писал:

если изменилась хотя бы одна переменная

Нет, даже хуже -все переменные отсылаются при каждом проходе этой функции. Условие if lSIOC_SendString ~= «» then будет истинно всегда, ибо перед этим мы заносили в строку значения переменных: lSIOC_SendString = lSIOC_SendString..R0. Тут бы добавить глобальную переменную lSIOC_SendStringPrev и сравнивать с ней, а обновлять ее значение после отправки… Но лучше массив, цикл и отправлять только действительно изменившиеся переменные..

САБ (09 September 2012 — 02:17) писал:

Если этот язык программирования поддерживает массивы и циклы

Это скрипт на Lua. Значит поддерживаются ассоциативные массивы (таблицы), циклы и ещё много чего.

САБ (09 September 2012 — 02:17) писал:

Описание этого языка программирования доступно?

Доступно:
http://www.lua.ru/doc/
http://steps3d.narod…a-tutorial.html
http://www.script-coding.com/Lua.html
http://w3d.wiki-site….php/Основы_Lua
http://ltwood.wikido…ipting:lua-lang
http://symmetrica.net/lua/lua1.pdf

Вот этот кусок с присвоением переменных:

Spoiler

вроде можно сократить таким образом:

R0 = «0=»..math.floor(TAS + 0.5)..»:»
R1 = «1=»..math.floor(IAS + 0.5)..»:»
R2 = «2=»..math.floor(AOA*ToDegree + 0.5)..»:»
R3 = «3=»..math.floor(Gp.y + 0.5)..»:»
R4 = «4=»..math.floor(VV + 0.5)..»:»
R5 = «5=»..math.floor(altBar + 0.5)..»:»
R6 = «6=»..math.floor(altRad + 0.5)..»:»

Jeka_M (09 September 2012 — 03:58) писал:

Вот этот кусок с присвоением переменных:
вроде можно сократить таким образом:

R0 = «0=»..math.floor(TAS + 0.5)..»:»
R1 = «1=»..math.floor(IAS + 0.5)..»:»
R2 = «2=»..math.floor(AOA*ToDegree + 0.5)..»:»
R3 = «3=»..math.floor(Gp.y + 0.5)..»:»
R4 = «4=»..math.floor(VV + 0.5)..»:»
R5 = «5=»..math.floor(altBar + 0.5)..»:»
R6 = «6=»..math.floor(altRad + 0.5)..»:»

Спасибо! Работает!!!

Прикрепленные изображения

  • Sioc1.JPG

Если принцип функционирования скрипта оставить как есть, то ещё вот этот фрагмент:

ra3dcs (08 September 2012 — 21:02) писал:

— записываемстрочку
lSIOC_SendString = lSIOC_SendString..R0
lSIOC_SendString = lSIOC_SendString..R1
lSIOC_SendString = lSIOC_SendString..R2
lSIOC_SendString = lSIOC_SendString..R3
lSIOC_SendString = lSIOC_SendString..R4
lSIOC_SendString = lSIOC_SendString..R5
lSIOC_SendString = lSIOC_SendString..R6

можно сократить вот так:
lSIOC_SendString = lSIOC_SendString..R0..R1..R2..R3..R4..R5..R6

или даже так:
lSIOC_SendString = R0..R1..R2..R3..R4..R5..R6
(Если предполагается, что раньше этого фрагмента НЕ будет присвоено какое-то значение переменной lSIOC_SendString, которое также нужно добавить в отправляемую строку. В данном скрипте так и есть, эта переменная объявляется пустой в функции LuaExportAfterNextFrame() )

———
Если же количество пересылаемых данных влияет на производительность, то как писал ув. САБ, можно подумать и попробовать переделать скрипт так, чтобы отправлялись только те переменные, которые поменяли своё значение, а не все подряд. Это при условии, что SIOC не обязательно каждый раз пересылать значение переменной, если оно не изменилось. Я с SIOC и с «железными кокпитами» вообще не сталкивался, поэтому не уверен.

Сообщение отредактировал Jeka_M: 09 September 2012 — 13:41

Jeka_M (09 September 2012 — 13:22) писал:

можно сократить вот так:
lSIOC_SendString = lSIOC_SendString..R0..R1..R2..R3..R4..R5..R6

Спасибо!
Подскажите максимальная длинна строки переменной какая может быть?

ra3dcs (09 September 2012 — 18:19) писал:

Подскажите максимальная длинна строки переменной какая может быть?

К сожалению не могу подсказать, нигде не нашёл такой информации. Нужно пробовать.

ra3dcs (10 September 2012 — 14:47) писал:

но с Иваном на эту тему говорить бесполезно, не создан он для общения с коллегами.

Странно, у меня сложилось противоположное мнение.

ra3dcs (10 September 2012 — 14:47) писал:

По «разведданным» эта программа есть еще у Сергея (Саб) может он, что подскажет в этом направлении?

Основные параметры полета удалось получить, за исключением сигнальных лампочек и вооружения.
Функция вывода примерно будет выглядеть пока так.
function LuaExportAfterNextFrame()
    local lSIOC_SendString = «»
    local TAS = LoGetTrueAirSpeed()* 3.6 — значение истинной скорости
    local IAS = LoGetIndicatedAirSpeed()* 3.6     — значение приборной скорости  
    local AOA = LoGetAngleOfAttack() — угол атаки
    local Gp  = LoGetAccelerationUnits()     — перегрузка
    local VV  = LoGetVerticalVelocity()   — верт. скорость
    local altBar = LoGetAltitudeAboveSeaLevel()   — высота баром.
    local altRad = LoGetAltitudeAboveGroundLevel()   — радио высота
    local pitch, bank, yaw = LoGetADIPitchBankYaw()  — крен, тангаж, курс.
    local Mach = LoGetMachNumber()


    local Magn_yaw = LoGetMagneticYaw()  — ????

    local HSI    = LoGetControlPanel_HSI()  — ????
    local engine = LoGetEngineInfo()  — параметры двигателя температура, обороты, топливо, гидро.
    local route = LoGetRoute()
    local PlaneID = LoGetPlayerPlaneId()
    local obj = LoGetObjectById(PlaneID)
    local myXCoord, myZCoord = getXYCoords(obj.LatLongAlt.Lat, obj.LatLongAlt.Long)
    local navi = LoGetNavigationInfo()


    — формируем строчку номер переменной = значение

    — округляем значение до целого
    R0 = «0=»..math.floor(TAS + 0.5)..»:»
    R1 = «1=»..math.floor(IAS + 0.5)..»:»
    R2 = «2=»..math.floor(AOA*ToDegree + 0.5)..»:»
    R3 = «3=»..math.floor(Gp.y + 0.5)..»:»
    R4 = «4=»..math.floor(VV + 0.5)..»:»
    R5 = «5=»..math.floor(altBar + 0.5)..»:»
    R6 = «6=»..math.floor(altRad + 0.5)..»:»
     R7 = «7=»..math.floor(HSI.Compass + 0.5)..»:»
    R8 = «8=»..math.floor(engine.RPM.left + 0.5)..»:»
    R9 = «9=»..math.floor(engine.RPM.right + 0.5)..»:»
    R10 = «10=»..math.floor(engine.Temperature.left + 0.5)..»:»
    R11 = «11=»..math.floor(engine.Temperature.right + 0.5)..»:»
    R12 = «12=»..math.floor(pitch*ToDegree + 0.5)..»:»
    R13 = «13=»..math.floor(bank*ToDegree + 0.5)..»:»
    R14 = «14=»..math.floor(yaw*ToDegree + 0.5)..»:»
    R15 = «15=»..math.floor(Magn_yaw*ToDegree + 0.5)..»:»
    R16 = «16=»..math.floor(engine.fuel_internal + 0.5)..»:»
    R17 = «17=»..math.floor(engine.fuel_external + 0.5)..»:»
    R18 = «18=»..math.floor(engine.HydraulicPressure.left + 0.5)..»:»
    R19 = «19=»..math.floor(engine.HydraulicPressure.right + 0.5)..»:»
    R20 = «20=»..math.floor(navi.Requirements.roll + 0.5)..»:»
    R21 = «21=»..math.floor(navi.Requirements.pitch + 0.5)..»:»
    R22 = «22=»..math.floor(navi.Requirements.altitude + 0.5)..»:»
    R23 = «23=»..math.floor(myXCoord + 0.5)..»:»
    R24 = «24=»..math.floor(obj.LatLongAlt.Alt + 0.5)..»:»
    R25 = «25=»..math.floor(myZCoord + 0.5)..»:»
    — записываем строчку
    lSIOC_SendString = lSIOC_SendString..R0..R1..R2..R3..R4..R5..R6..R7..R8..R9..R10..R11..R12..R13..R14..R15..R16..R17..R18..R19..R20..R21..R22..R23..R24..R25
    — отправляем
    if lSIOC_SendString ~= «» then
    socket.try(c:send(«Arn.Resp:»..lSIOC_SendString..»n»))
    end
end
————————————————————————————————
Однако получить данные от Локк Она оказалось еще пол дела, теперь нужно эти переменные вытащить из SIOС-а.
Иван (Sly) написал программу клиент, но с Иваном на эту тему говорить бесполезно, не создан он для общения с коллегами.
По «разведданным» эта программа есть еще у Сергея (Саб) может он, что подскажет в этом направлении?

LISА (10 September 2012 — 18:12) писал:

рабочий код приводить не буду, лень искать (для лок она это все равно не атуально)

Жаль! Пригодилось бы!

Вот записали меня во враги народа )))
Не помню чтобы я кому-нибудь отказывал…

Александр и и не помню чтобы вы обращались ко мне с подобной просьбой.
Собственно прокладку можете взять у Сергея, могу отправить я как первоисточник )

С Уважением, Иван.

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

function LuaExportActivityNextEvent(t)
  local tNext = t
  ......
  tNext = tNext + 0.1  --  сдесь задается временнной интервал в секундах 
  return tNext
end

кроме того массив состояния сигнальных ламп можно (даже нужно!) обработать с помощью цикла,
рабочий код приводить не буду, лень искать (для лок она это все равно не атуально)

ra3dcs (13 September 2012 — 14:22) писал:

Соединение с сервером есть

Александр, еще чуть-чуть и сможете наконец выбросить свой комп с виндовсом МЕ :sarcastic:

При технической поддержки Евгения (Shaman_2010) попытаемся встроить подпрограмму чтения переменных от сервера SIOC. В программу управления приборами.
Язык программирования Delphi.

В строку подключаемых модулей добавляем модуль Sockets.

И на существующую форму добавляем объект ClientSocket . Так же нам еще понадобятся три кнопки  и два поля редактирования  компоненты Edit для ввода адреса сервера и номера порта,

Прикрепленные изображения

  • Sioc2.JPG
  • Sioc3.JPG

Процедура Обработки события при нажатии кнопки «Connect Button.

                           procedure TForm1.Connect_Buttonclick(Sender: TObject);
begin
   ClientSocket.Address := Adress_SIOC.Text;
   ClientSocket.Host := Adress_SIOC.Text;
   ClientSocket.Port := StrToInt(Port_SIOC.Text);
   ClientSocket.Active := True;
end;

Процедура обработки сообщения об ошибках:


procedure TForm1.ClientSocketError(Sender: TObject;

  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
  var ErrorCode: Integer);
begin
  if ErrorCode = 10061 then begin
    ShowMessage(‘Сервер не найден!’);
    ErrorCode := 0;
  end else begin
    ShowMessage(‘Неизвестная ошибка!’);
    ErrorCode := 0;
  end;
end;




Процедура чтения данных с сервера:



procedure TForm1.ClientSocketRead(Sender: TObject;

  Socket: TCustomWinSocket);
var
  sBuffer: string;
  iPos, iVar, iVal: integer;
begin
sBuffer := Socket.ReceiveText;
  if pos(‘Resp’, sBuffer) <> 0 then begin
    sBuffer := StringReplace(sBuffer, ‘Arn.Vivo:’, », [rfReplaceAll]);
    sBuffer := StringReplace(sBuffer, ‘Arn.Resp:’, », [rfReplaceAll]);
    sBuffer := StringReplace(sBuffer, #$D + #$A, », [rfReplaceAll]);
    if length(sBuffer) > 0 then begin
  while (length(sBuffer) > 1) do begin
    iPos := pos(‘=’, sBuffer);
    iVar := StrToInt(copy(sBuffer, 1, iPos — 1));
    delete(sBuffer, 1, iPos);
    iPos := pos(‘:’, sBuffer);
    iVal := StrToInt(copy(sBuffer, 1, iPos — 1));
    delete(sBuffer, 1, iPos);
    Log_window.Lines.Add(IntToStr(iVar) + ‘==’ + IntToStr(iVal))
  end;
    end;
  end;
  sBuffer := »;
end;

Процедура обработки соединения.


procedure TForm1.ClientSocketConnect(Sender: TObject;

  Socket: TCustomWinSocket);
begin
  Connect_Button.Enabled := False;
  Disconnect_Button.Enabled := True;
  Button1.Enabled := True;
  ClientSocket.Socket.SendText(SIOC_Hello);
end;
Процедура отработки нажатия кнопки запрос данных:


procedure TForm1.Button1Click(Sender: TObject);

begin
  SIOC_Quest := ‘0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20‘;
  SIOC_Vars := ‘Arn.Inicio:’ + SIOC_Quest + #$D + #$A;
  ClientSocket.Socket.SendText(SIOC_Vars);
  SIOC_Vars := »;
end;
Процедура нажатия кнопки Disconnekt и отключение от  сервера.


procedure TForm1.ClientSocketDisconnect(Sender: TObject;

  Socket: TCustomWinSocket);
begin
  Connect_Button.Enabled := True;
  Disconnect_Button.Enabled := False;
  Button1.Enabled := False;
end;


procedure TForm1.Disconnect_Buttonclick(Sender: TObject);

begin
   ClientSocket.Socket.SendText(‘Arn.Fin’ + #$D + #$A);
   ClientSocket.Active := False;
end;
Что получилось в итоге?
Соединение с сервером есть, данные получаю, но использовать пока не могу, так как переменные iVar , iVal  оказались локальные и доступны пока в процедуре чтения данных.

Прикрепленные изображения

  • Sioc4.JPG

САБ (13 September 2012 — 15:39) писал:

Александр, еще чуть-чуть и сможете наконец выбросить свой комп с виндовсом МЕ :sarcastic:

Пока еще такого сделать не могу!!!!!

1 2