forked from CPL/Simulator
138 lines
4.3 KiB
C++
138 lines
4.3 KiB
C++
// joypad.h ───────────────────────────────────────────────────────────
|
||
#pragma once
|
||
#include <iostream>
|
||
#include <iomanip>
|
||
#include <Windows.h> // <- для AllocConsole
|
||
#using <System.dll>
|
||
|
||
using namespace System;
|
||
using namespace System::IO::Ports;
|
||
using namespace System::Threading;
|
||
|
||
/*---------------------------------------------------------------------
|
||
Joypad (i-Bus → COM-порт)
|
||
---------------------------------------------------------------------*/
|
||
public ref class Joypad
|
||
{
|
||
public:
|
||
/* событие – _не подписывайтесь_ ➜ ничего в форме не вызовется */
|
||
delegate void TickHandler(array<UInt16>^ ch);
|
||
event TickHandler^ TickEvent;
|
||
|
||
Joypad()
|
||
{
|
||
// -------- открываем консоль один раз ----------------------
|
||
static bool consoleReady = false;
|
||
if (!consoleReady && ::AllocConsole())
|
||
{
|
||
FILE* fp;
|
||
freopen_s(&fp, "CONOUT$", "w", stdout);
|
||
std::cout << " CH1 CH2 CH3 CH4 CH5 CH6\n";
|
||
consoleReady = true;
|
||
}
|
||
// ----------------------------------------------------------
|
||
|
||
_sp = gcnew SerialPort();
|
||
_sp->ReadTimeout = 200;
|
||
_sp->ReadBufferSize = 256;
|
||
}
|
||
|
||
/* ---------- запуск ------------------------------------------- */
|
||
bool Start(String^ port, int baud )
|
||
{
|
||
if (_run) return true;
|
||
try
|
||
{
|
||
_sp->PortName = port;
|
||
_sp->BaudRate = baud;
|
||
_sp->Parity = Parity::None;
|
||
_sp->DataBits = 8;
|
||
_sp->StopBits = StopBits::One;
|
||
_sp->Open();
|
||
}
|
||
catch (Exception^ ex)
|
||
{
|
||
System::Diagnostics::Debug::WriteLine("Serial error: " + ex->Message);
|
||
return false;
|
||
}
|
||
|
||
_run = true;
|
||
_thr = gcnew Thread(gcnew ThreadStart(this, &Joypad::RxLoop));
|
||
_thr->IsBackground = true;
|
||
_thr->Start();
|
||
return true;
|
||
}
|
||
|
||
/* ---------- остановка ---------------------------------------- */
|
||
void Stop()
|
||
{
|
||
_run = false;
|
||
if (_thr && _thr->IsAlive) _thr->Join();
|
||
if (_sp->IsOpen) _sp->Close();
|
||
}
|
||
|
||
property array<UInt16>^ Channels { array<UInt16>^ get() { return _ch; } }
|
||
|
||
private:
|
||
// ----------- поток приёма i-Bus ---------------------------------
|
||
void RxLoop()
|
||
{
|
||
array<Byte>^ buf = gcnew array<Byte>(64);
|
||
array<Byte>^ pkt = gcnew array<Byte>(32);
|
||
|
||
while (_run)
|
||
{
|
||
int read = 0;
|
||
try { read = _sp->Read(buf, 0, buf->Length); }
|
||
catch (TimeoutException^) { continue; }
|
||
|
||
for (int i = 0; i < read; ++i)
|
||
{
|
||
_fifo[_head++] = buf[i];
|
||
if (_head >= _fifo->Length) _head = 0;
|
||
|
||
if (_fifo[_head] == 0x20 && _fifo[(_head + 1) & 0x7F] == 0x40)
|
||
{
|
||
for (int j = 0; j < 32; ++j)
|
||
pkt[j] = _fifo[(_head + j) & 0x7F];
|
||
|
||
if (!CheckPkt(pkt)) continue;
|
||
ParseChannels(pkt);
|
||
|
||
// -------- 1) печать в консоль ----------------
|
||
std::cout << '\r';
|
||
for (int k = 0; k < 6; ++k)
|
||
std::cout << std::setw(6) << _ch[k] << ' ';
|
||
std::cout << std::flush;
|
||
// ---------------------------------------------
|
||
|
||
// -------- 2) необязательный вызов события ----
|
||
TickEvent(_ch); // raise
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
bool CheckPkt(array<Byte>^ p)
|
||
{
|
||
UInt16 sum = 0; for (int i = 0; i < 30; ++i) sum += p[i];
|
||
sum = 0xFFFF - sum;
|
||
return sum == (p[30] | p[31] << 8);
|
||
}
|
||
|
||
void ParseChannels(array<Byte>^ p)
|
||
{
|
||
for (int i = 0; i < 14; ++i)
|
||
_ch[i] = (UInt16)(p[2 + i * 2] | p[3 + i * 2] << 8);
|
||
}
|
||
|
||
/* ---------- поля --------------------------------------------- */
|
||
SerialPort^ _sp;
|
||
Thread^ _thr;
|
||
bool _run = false;
|
||
|
||
array<UInt16>^ _ch = gcnew array<UInt16>(14);
|
||
array<Byte>^ _fifo = gcnew array<Byte>(128);
|
||
int _head = 0;
|
||
};
|