diff --git a/Button.cpp b/Button.cpp index 0232af5c..aefcc35a 100644 --- a/Button.cpp +++ b/Button.cpp @@ -156,5 +156,6 @@ TButton::play( sound* Sound ) { Sound->stop(); Sound->play(); + return; } diff --git a/Button.h b/Button.h index c468d7ff..3080aff0 100644 --- a/Button.h +++ b/Button.h @@ -49,6 +49,7 @@ class TButton Turn( !m_state ); }; inline bool Active() { return (pModelOn) || (pModelOff); }; + inline uint8_t b() { return m_state ? 1 : 0; }; void Update(); void Init(std::string const &asName, TModel3d *pModel, bool bNewOn = false); void Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2 = NULL); diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a15a354..e42703ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ include(PrecompiledHeader) set(DEPS_DIR ${DEPS_DIR} "${CMAKE_SOURCE_DIR}/ref") project("eu07") +set(CMAKE_CXX_STANDARD 14) include_directories("." "Console" "McZapkie") file(GLOB HEADERS "*.h" "Console/*.h" "McZapkie/*.h") @@ -70,6 +71,7 @@ set(SOURCES "material.cpp" "lua.cpp" "stdafx.cpp" +"uart.cpp" ) set (ARCH "x86") @@ -78,7 +80,7 @@ if (WIN32) add_definitions(-DHAVE_ROUND) # to make pymath to not redefine round add_definitions(-DEU07_BUILD_STATIC) # to make pymath to not redefine round - set(SOURCES ${SOURCES} "windows.cpp" "Console.cpp" "Console/LPT.cpp" "Console/MWD.cpp" "Console/PoKeys55.cpp" "wavread.cpp") + set(SOURCES ${SOURCES} "windows.cpp" "Console.cpp" "Console/LPT.cpp" "Console/PoKeys55.cpp" "wavread.cpp") set(GLEW_INCLUDE_DIR ${GLEW_INCLUDE_DIR} "${DEPS_DIR}/glew/include/") set(GLFW3_INCLUDE_DIR ${GLFW3_INCLUDE_DIR} "${DEPS_DIR}/glfw/include/") set(GLUT_INCLUDE_DIR ${GLUT_INCLUDE_DIR} "${DEPS_DIR}/freeglut/include/") @@ -193,8 +195,12 @@ find_package(LuaJIT REQUIRED) include_directories(${LUAJIT_INCLUDE_DIR}) target_link_libraries(${PROJECT_NAME} ${LUAJIT_LIBRARIES}) +find_package(libserialport REQUIRED) +include_directories(${libserialport_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} ${libserialport_LIBRARY}) + if (UNIX) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -g") endif() #cotire(${PROJECT_NAME}) diff --git a/CMake_modules/Findlibserialport.cmake b/CMake_modules/Findlibserialport.cmake new file mode 100644 index 00000000..08548e8a --- /dev/null +++ b/CMake_modules/Findlibserialport.cmake @@ -0,0 +1,9 @@ +find_path(libserialport_INCLUDE_DIR libserialport.h) +find_library(libserialport_LIBRARY serialport) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libserialport DEFAULT_MSG libserialport_LIBRARY libserialport_INCLUDE_DIR) +if(libserialport_FOUND) + set(libserialport_LIBRARIES ${libserialport_LIBRARY}) + set(libserialport_INCLUDE_DIRS ${libserialport_INCLUDE_DIR}) +endif() + diff --git a/Console.cpp b/Console.cpp index 0f8775c5..886f2656 100644 --- a/Console.cpp +++ b/Console.cpp @@ -13,7 +13,6 @@ http://mozilla.org/MPL/2.0/. #include "McZapkie/mctools.h" #include "LPT.h" #include "Logs.h" -#include "MWD.h" // maciek001: obsluga portu COM #include "PoKeys55.h" //--------------------------------------------------------------------------- @@ -118,7 +117,6 @@ Console::~Console() { delete PoKeys55[0]; delete PoKeys55[1]; - delete MWDComm; }; void Console::ModeSet(int m, int h) @@ -166,18 +164,6 @@ int Console::On() break; } - if (Global::bMWDmasterEnable) - { - WriteLog("Opening ComPort"); - MWDComm = new TMWDComm(); - if (!(MWDComm->Open())) // jeżeli nie otwarł portu - { - WriteLog("ERROR: ComPort is NOT OPEN!"); - delete MWDComm; - MWDComm = NULL; - } - } - return 0; }; @@ -196,8 +182,6 @@ void Console::Off() PoKeys55[1] = NULL; delete LPT; LPT = NULL; - delete MWDComm; - MWDComm = NULL; }; void Console::BitsSet(int mask, int entry) @@ -289,56 +273,6 @@ void Console::BitsUpdate(int mask) } break; } - if (Global::bMWDmasterEnable) - { - // maciek001: MWDComm lampki i kontrolki - // out3: ogrzewanie sk?adu, opory rozruchowe, poslizg, zaluzjewent, -, -, czuwak, shp - // out4: stycz.liniowe, pezekaznikr??nicobwpomoc, nadmiarprzetw, roznicowy obw. g?, nadmiarsilniki, wylszybki, zanikpr?duprzyje?dzienaoporach, nadmiarsprezarki - // out5: HASLER */ - if (mask & 0x0001) if (iBits & 1) { - MWDComm->WriteDataBuff[4] |= 1 << 7; // SHP HASLER też - if (!MWDComm->bSHPstate) { - MWDComm->bSHPstate = true; - MWDComm->bPrzejazdSHP = true; - } - else MWDComm->bPrzejazdSHP = false; - } - else { - MWDComm->WriteDataBuff[4] &= ~(1 << 7); - MWDComm->bPrzejazdSHP = false; - MWDComm->bSHPstate = false; - } - if (mask & 0x0002) if (iBits & 2) MWDComm->WriteDataBuff[4] |= 1 << 6; // CA - else MWDComm->WriteDataBuff[4] &= ~(1 << 6); - if (mask & 0x0004) if (iBits & 4) MWDComm->WriteDataBuff[4] |= 1 << 1; // jazda na oporach rozruchowych - else MWDComm->WriteDataBuff[4] &= ~(1 << 1); - if (mask & 0x0008) if (iBits & 8) MWDComm->WriteDataBuff[5] |= 1 << 5; // wyłącznik szybki - else MWDComm->WriteDataBuff[5] &= ~(1 << 5); - if (mask & 0x0010) if (iBits & 0x10) MWDComm->WriteDataBuff[5] |= 1 << 4; // nadmiarowy silników trakcyjnych - else MWDComm->WriteDataBuff[5] &= ~(1 << 4); - if (mask & 0x0020) if (iBits & 0x20) MWDComm->WriteDataBuff[5] |= 1 << 0; // styczniki liniowe - else MWDComm->WriteDataBuff[5] &= ~(1 << 0); - if (mask & 0x0040) if (iBits & 0x40) MWDComm->WriteDataBuff[4] |= 1 << 2; // poślizg - else MWDComm->WriteDataBuff[4] &= ~(1 << 2); - if (mask & 0x0080) if (iBits & 0x80) MWDComm->WriteDataBuff[5] |= 1 << 2; // (nadmiarowy) przetwornicy? ++ - else MWDComm->WriteDataBuff[5] &= ~(1 << 2); - if (mask & 0x0100) if (iBits & 0x100) MWDComm->WriteDataBuff[5] |= 1 << 7; // nadmiarowy sprężarki - else MWDComm->WriteDataBuff[5] &= ~(1 << 7); - if (mask & 0x0200) if (iBits & 0x200) MWDComm->WriteDataBuff[2] |= 1 << 1; // wentylatory i opory - else MWDComm->WriteDataBuff[2] &= ~(1 << 1); - if (mask & 0x0400) if (iBits & 0x400) MWDComm->WriteDataBuff[2] |= 1 << 2; // wysoki rozruch - else MWDComm->WriteDataBuff[2] &= ~(1 << 2); - if (mask & 0x0800) if (iBits & 0x800) MWDComm->WriteDataBuff[4] |= 1 << 0; // ogrzewanie pociągu - else MWDComm->WriteDataBuff[4] &= ~(1 << 0); - if (mask & 0x1000) if (iBits & 0x1000) MWDComm->bHamowanie = true; // hasler: ciśnienie w hamulcach HASLER rysik 2 - else MWDComm->bHamowanie = false; - if (mask & 0x2000) if (iBits & 0x2000) MWDComm->WriteDataBuff[6] |= 1 << 4; // hasler: prąd "na" silnikach - HASLER rysik 3 - else MWDComm->WriteDataBuff[6] &= ~(1 << 4); - if (mask & 0x4000) if (iBits & 0x4000) MWDComm->WriteDataBuff[6] |= 1 << 7; // brzęczyk SHP/CA - else MWDComm->WriteDataBuff[6] &= ~(1 << 7); - //if(mask & 0x8000) if(iBits & 0x8000) MWDComm->WriteDataBuff[1] |= 1<<7; (puste) - //else MWDComm->WriteDataBuff[0] &= ~(1<<7); - } }; void Console::ValueSet(int x, double y) @@ -371,57 +305,6 @@ void Console::ValueSet(int x, double y) WriteLog(" calibrated=" + std::to_string(temp)); PoKeys55[0]->PWM(x, temp); } - if (Global::bMWDmasterEnable) - { - unsigned int iliczba; - switch (x) - { - case 0: iliczba = (unsigned int)floor((y / (Global::fMWDzg[0] * 10) * Global::fMWDzg[1]) + 0.5); // zbiornik główny - MWDComm->WriteDataBuff[12] = (unsigned char)(iliczba >> 8); - MWDComm->WriteDataBuff[11] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Main tank press " + to_string(MWDComm->WriteDataBuff[12]) + " " + to_string(MWDComm->WriteDataBuff[11])); - break; - case 1: iliczba = (unsigned int)floor((y / (Global::fMWDpg[0] * 10) * Global::fMWDpg[1]) + 0.5); // przewód główny - MWDComm->WriteDataBuff[10] = (unsigned char)(iliczba >> 8); - MWDComm->WriteDataBuff[9] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Main pipe press " + to_string(MWDComm->WriteDataBuff[10]) + " " + to_string(MWDComm->WriteDataBuff[9])); - break; - case 2: iliczba = (unsigned int)floor((y / (Global::fMWDph[0] * 10) * Global::fMWDph[1]) + 0.5); // cylinder hamulcowy - MWDComm->WriteDataBuff[8] = (unsigned char)(iliczba >> 8); - MWDComm->WriteDataBuff[7] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Break press " + to_string(MWDComm->WriteDataBuff[8]) + " " + to_string(MWDComm->WriteDataBuff[7])); - break; - case 3: iliczba = (unsigned int)floor((y / Global::fMWDvolt[0] * Global::fMWDvolt[1]) + 0.5); // woltomierz WN - MWDComm->WriteDataBuff[14] = (unsigned char)(iliczba >> 8); - MWDComm->WriteDataBuff[13] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Hi Volt meter " + to_string(MWDComm->WriteDataBuff[14]) + " " + to_string(MWDComm->WriteDataBuff[13])); - break; - case 4: iliczba = (unsigned int)floor((y / Global::fMWDamp[0] * Global::fMWDamp[1]) + 0.5); // amp WN 1 - MWDComm->WriteDataBuff[16] = (unsigned char)(iliczba >> 8); - MWDComm->WriteDataBuff[15] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Apm meter1 " + to_string(MWDComm->WriteDataBuff[16]) + " " + to_string(MWDComm->WriteDataBuff[15])); - break; - case 5: iliczba = (unsigned int)floor((y / Global::fMWDamp[0] * Global::fMWDamp[1]) + 0.5); // amp WN 2 - MWDComm->WriteDataBuff[18] = (unsigned char)(iliczba >> 8); - MWDComm->WriteDataBuff[17] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Apm meter2 " + to_string(MWDComm->WriteDataBuff[18]) + " " + to_string(MWDComm->WriteDataBuff[17])); - break; - case 6: iliczba = (unsigned int)floor((y / Global::fMWDamp[0] * Global::fMWDamp[1]) + 0.5); // amp WN 3 - MWDComm->WriteDataBuff[20] = (unsigned int)(iliczba >> 8); - MWDComm->WriteDataBuff[19] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Apm meter3 " + to_string(MWDComm->WriteDataBuff[20]) + " " + to_string(MWDComm->WriteDataBuff[19])); - break; - case 7: if (Global::iPause) MWDComm->WriteDataBuff[0] = 0; //skoro pauza to hasler stoi i nie nabija kilometrów CHYBA NIE DZIAŁA! - else MWDComm->WriteDataBuff[0] = (unsigned char)floor(y); // prędkość dla np haslera - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Speed: " + to_string(MWDComm->WriteDataBuff[0])); - break; - case 8: iliczba = (unsigned int)floor((y / Global::fMWDlowVolt[0] * Global::fMWDlowVolt[1]) + 0.5); // volt NN - MWDComm->WriteDataBuff[22] = (unsigned int)(iliczba >> 8); - MWDComm->WriteDataBuff[21] = (unsigned char)iliczba; - if (Global::bMWDmasterEnable && Global::iMWDDebugMode & 8) WriteLog("Low Volt meter " + to_string(MWDComm->WriteDataBuff[22]) + " " + to_string(MWDComm->WriteDataBuff[21])); - break; // przygotowane do wdrożenia, jeszcze nie wywoływane - } - } }; void Console::Update() @@ -439,11 +322,6 @@ void Console::Update() Global::iPause |= 8; // tak??? PoKeys55[0]->Connect(); // próba ponownego podłączenia } - if (Global::bMWDmasterEnable) - { - if (MWDComm->GetMWDState()) MWDComm->Run(); - else MWDComm->Close(); - } }; float Console::AnalogGet(int x) @@ -468,11 +346,6 @@ float Console::AnalogCalibrateGet(int x) if (x == 1) return b/10; else return b; } - if (Global::bMWDmasterEnable && Global::bMWDBreakEnable) - { - float b = (float)MWDComm->uiAnalog[x]; - return (b - Global::fMWDAnalogInCalib[x][0]) / (Global::fMWDAnalogInCalib[x][1] - Global::fMWDAnalogInCalib[x][0]); - } return -1.0; // odcięcie }; diff --git a/Console/MWD.cpp b/Console/MWD.cpp deleted file mode 100644 index 67b63bdd..00000000 --- a/Console/MWD.cpp +++ /dev/null @@ -1,763 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -/* - Program obsługi portu COM i innych na potrzeby sterownika MWDevice - (oraz innych wykorzystujących komunikację przez port COM) - dla Symulatora Pojazdów Szynowych MaSzyna - author: Maciej Witek 2016 - Autor nie ponosi odpowiedzialności za niewłaciwe używanie lub działanie programu! -*/ -#include "stdafx.h" -#include "MWD.h" -#include "Globals.h" -#include "Logs.h" -#include "World.h" - -#include - -HANDLE hComm; - -TMWDComm::TMWDComm() // konstruktor -{ - MWDTime = 0; - bSHPstate = false; - bPrzejazdSHP = false; - bKabina1 = true; // pasuje wyciągnąć dane na temat kabiny - bKabina2 = false; // i ustawiać te dwa parametry! - bHamowanie = false; - bCzuwak = false; - - bRysik1H = false; - bRysik1L = false; - bRysik2H = false; - bRysik2L = false; - - bocznik = 0; - nastawnik = 0; - kierunek = 0; - bnkMask = 0; - - int i = 6; - - while (i) - { - i--; - lastStateData[i] = 0; - maskData[i] = 0; - maskSwitch[i] = 0; - bitSwitch[i] = 0; - } - - i = 0; - while (i 0) - return 1; - else - return 0; -} - -bool TMWDComm::ReadData() // odbieranie danych + odczyta danych analogowych i zapis do zmiennych -{ - DWORD bytes_read; - ReadFile(hComm, &ReadDataBuff[0], BYTETOREAD, &bytes_read, NULL); - if (Global::bMWDdebugEnable && Global::iMWDDebugMode == 128) WriteLog("Data receive. Checking data..."); - if (Global::bMWDBreakEnable) - { - uiAnalog[0] = (ReadDataBuff[9] << 8) + ReadDataBuff[8]; - uiAnalog[1] = (ReadDataBuff[11] << 8) + ReadDataBuff[10]; - uiAnalog[2] = (ReadDataBuff[13] << 8) + ReadDataBuff[12]; - uiAnalog[3] = (ReadDataBuff[15] << 8) + ReadDataBuff[14]; - if (Global::bMWDdebugEnable && (Global::iMWDDebugMode & 1)) - { - WriteLog("Main Break = " + to_string(uiAnalog[0])); - WriteLog("Locomotiv Break = " + to_string(uiAnalog[1])); - } - if (Global::bMWDdebugEnable && (Global::iMWDDebugMode & 2)) - { - WriteLog("Analog input 1 = " + to_string(uiAnalog[2])); - WriteLog("Analog imput 2 = " + to_string(uiAnalog[3])); - } - } - if (Global::bMWDInputEnable) CheckData(); - return TRUE; -} - -bool TMWDComm::SendData() // wysyłanie danych -{ - DWORD bytes_write; - - WriteFile(hComm, &WriteDataBuff[0], BYTETOWRITE, &bytes_write, NULL); - - return TRUE; -} - -bool TMWDComm::Run() // wywoływanie obsługi MWD + generacja większego opóźnienia -{ - if (GetMWDState()) - { - MWDTime++; - if (!(MWDTime % Global::iMWDdivider)) - { - MWDTime = 0; - if (Global::bMWDdebugEnable && Global::iMWDDebugMode == 128) WriteLog("Sending data..."); - SendData(); - if (Global::bMWDdebugEnable && Global::iMWDDebugMode == 128) WriteLog(" complet!\nReceiving data..."); - ReadData(); - if (Global::bMWDdebugEnable && Global::iMWDDebugMode == 128) WriteLog(" complet!"); - return 1; - } - } - else - { - WriteLog("Port COM: connection ERROR!"); - Close(); - // może spróbować się połączyć znowu? - return 0; - } - return 1; -} - -void TMWDComm::CheckData() // sprawdzanie wejść cyfrowych i odpowiednie sterowanie maszyną -{ - int i = 0; - while (i < 6) - { - maskData[i] = ReadDataBuff[i] ^ lastStateData[i]; - lastStateData[i] = ReadDataBuff[i]; - i++; - } - /* - Rozpiska portów! - Port0: 0 NC odblok. przek. sprężarki i wentyl. oporów - 1 M wyłącznik wył. szybkiego - 2 Shift+M impuls załączający wył. szybki - 3 N odblok. przekaźników nadmiarowych - i różnicowego obwodu głównegoMMm - 4 NC rezerwa - 5 Ctrl+N odblok. przek. nadmiarowych - przetwornicy, ogrzewania pociągu i różnicowych obw. pomocniczych - 6 L wył. styczników liniowych - 7 SPACE kasowanie czuwaka - - Port1: 0 NC - 1 (Shift) X przetwornica - 2 (Shift) C sprężarka - 3 S piasecznice - 4 (Shift) H ogrzewanie składu - 5 przel. hamowania Shift+B - pspbpwy Ctrl+B pospieszny B towarowy - 6 przel. hamowania - 7 (Shift) F rozruch w/n - - Port2: 0 (Shift) P pantograf przedni - 1 (Shift) O pantograf tylni - 2 ENTER przyhamowanie przy poślizgu - 3 () przyciemnienie świateł - 4 () przyciemnienie świateł - 5 NUM6 odluźniacz - 6 a syrena lok W - 7 A syrena lok N - - Port3: 0 Shift+J bateria - 1 - 2 - 3 - 4 - 5 - 6 - 7 - */ - - /* po przełączeniu bistabilnego najpierw wciskamy klawisz i przy następnym - wejściu w pętlę MWD puszczamy bo inaczej nie działa - */ - - // wciskanie przycisków klawiatury - /*PORT0*/ - if (maskData[0] & 0x02) if (lastStateData[0] & 0x02) - KeyBoard('M', 1); // wyłączenie wyłącznika szybkiego - else KeyBoard('M', 0); // monostabilny - if (maskData[0] & 0x04) if (lastStateData[0] & 0x04) // impuls załączający wyłącznik szybki - { - KeyBoard(0x10, 1); // monostabilny - KeyBoard('M', 1); - } - else - { - KeyBoard('M', 0); - KeyBoard(0x10, 0); - } - if (maskData[0] & 0x08) if (lastStateData[0] & 0x08) - KeyBoard('N', 1); // odblok nadmiarowego silników trakcyjnych - else KeyBoard('N', 0); // monostabilny - if (maskData[0] & 0x20) if (lastStateData[0] & 0x20) - { // odblok nadmiarowego przetwornicy, ogrzewania poc. - KeyBoard(0x11, 1); // różnicowego obwodów pomocniczych - KeyBoard('N', 1); // monostabilny - } - else - { - KeyBoard('N', 0); - KeyBoard(0x11, 0); - } - if (maskData[0] & 0x40) if (lastStateData[0] & 0x40) KeyBoard('L', 1); // wył. styczników liniowych - else KeyBoard('L', 0); // monostabilny - if (maskData[0] & 0x80) if (lastStateData[0] & 0x80) KeyBoard(0x20, 1); // kasowanie czuwaka/SHP - else KeyBoard(0x20, 0); // kasowanie czuwaka/SHP - - /*PORT1*/ - - // puszczanie przycisku bistabilnego klawiatury - if (maskSwitch[1] & 0x02) - { - if (bitSwitch[1] & 0x02) - { - KeyBoard('X', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('X', 0); - maskSwitch[1] &= ~0x02; - } - if (maskSwitch[1] & 0x04) { - if (bitSwitch[1] & 0x04) { - KeyBoard('C', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('C', 0); - maskSwitch[1] &= ~0x04; - } - if (maskSwitch[1] & 0x10) { - if (bitSwitch[1] & 0x10) { - KeyBoard('H', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('H', 0); - maskSwitch[1] &= ~0x10; - } - if (maskSwitch[1] & 0x20 || maskSwitch[1] & 0x40) { - if (maskSwitch[1] & 0x20) KeyBoard(0x10, 0); - if (maskSwitch[1] & 0x40) KeyBoard(0x11, 0); - KeyBoard('B', 0); - maskSwitch[1] &= ~0x60; - } - if (maskSwitch[1] & 0x80) { - if (bitSwitch[1] & 0x80) { - KeyBoard('F', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('F', 0); - maskSwitch[1] &= ~0x80; - } - - // przetwornica - if (maskData[1] & 0x02) if (lastStateData[1] & 0x02) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('X', 1); - maskSwitch[1] |= 0x02; - bitSwitch[1] |= 0x02; - } - else - { - maskSwitch[1] |= 0x02; - bitSwitch[1] &= ~0x02; - KeyBoard('X', 1); - } - // sprężarka - if (maskData[1] & 0x04) if (lastStateData[1] & 0x04) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('C', 1); - maskSwitch[1] |= 0x04; - bitSwitch[1] |= 0x04; - } - else - { - maskSwitch[1] |= 0x04; - bitSwitch[1] &= ~0x04; - KeyBoard('C', 1); - } - // piasecznica - if (maskData[1] & 0x08) if (lastStateData[1] & 0x08) - KeyBoard('S', 1); - else - KeyBoard('S', 0); // monostabilny - // ogrzewanie składu - if (maskData[1] & 0x10) if (lastStateData[1] & 0x10) - { - KeyBoard(0x11, 1); // bistabilny - KeyBoard('H', 1); - maskSwitch[1] |= 0x10; - bitSwitch[1] |= 0x10; - } - else - { - maskSwitch[1] |= 0x10; - bitSwitch[1] &= ~0x10; - KeyBoard('H', 1); - } - // przełącznik hamowania - if (maskData[1] & 0x20 || maskData[1] & 0x40) - { - if (lastStateData[1] & 0x20) - { // Shift+B - KeyBoard(0x10, 1); - maskSwitch[1] |= 0x20; - } - else if (lastStateData[1] & 0x40) - { // Ctrl+B - KeyBoard(0x11, 1); - maskSwitch[1] |= 0x40; - } - KeyBoard('B', 1); - } - // rozruch wysoki/niski - if (maskData[1] & 0x80) if (lastStateData[1] & 0x80) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('F', 1); - maskSwitch[1] |= 0x80; - bitSwitch[1] |= 0x80; - } - else - { - maskSwitch[1] |= 0x80; - bitSwitch[1] &= ~0x80; - KeyBoard('F', 1); - } - - - //PORT2 - if (maskSwitch[2] & 0x01) - { - if (bitSwitch[2] & 0x01) - { - KeyBoard('P', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('P', 0); - maskSwitch[2] &= ~0x01; - } - if (maskSwitch[2] & 0x02) - { - if (bitSwitch[2] & 0x02) - { - KeyBoard('O', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('O', 0); - maskSwitch[2] &= ~0x02; - } - - // pantograf przedni - if (maskData[2] & 0x01) if (lastStateData[2] & 0x01) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('P', 1); - maskSwitch[2] |= 0x01; - bitSwitch[2] |= 0x01; - } - else - { - maskSwitch[2] |= 0x01; - bitSwitch[2] &= ~0x01; - KeyBoard('P', 1); - } - // pantograf tylni - if (maskData[2] & 0x02) if (lastStateData[2] & 0x02) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('O', 1); - maskSwitch[2] |= 0x02; - bitSwitch[2] |= 0x02; - } - else - { - maskSwitch[2] |= 0x02; - bitSwitch[2] &= ~0x02; - KeyBoard('O', 1); - } - // przyhamowanie przy poślizgu - if (maskData[2] & 0x04) if (lastStateData[2] & 0x04) { - KeyBoard(0x10, 1); // monostabilny - KeyBoard(0x0D, 1); - } - else - { - KeyBoard(0x0D, 0); - KeyBoard(0x10, 0); - } - /*if(maskData[2] & 0x08) if (lastStateData[2] & 0x08){ // przyciemnienie świateł - KeyBoard(' ',0); // bistabilny - KeyBoard(0x10,1); - KeyBoard(' ',1); - }else{ - KeyBoard(' ',0); - KeyBoard(0x10,0); - KeyBoard(' ',1); - } - if(maskData[2] & 0x10) if (lastStateData[2] & 0x10) { // przyciemnienie świateł - KeyBoard(' ',0); // bistabilny - KeyBoard(0x11,1); - KeyBoard(' ',1); - }else{ - KeyBoard(' ',0); - KeyBoard(0x11,0); - KeyBoard(' ',1); - }*/ - // odluźniacz - if (maskData[2] & 0x20) if (lastStateData[2] & 0x20) - KeyBoard(0x66, 1); - else - KeyBoard(0x66, 0); // monostabilny - // syrena wysoka - if (maskData[2] & 0x40) if (lastStateData[2] & 0x40) - { - KeyBoard(0x10, 1); // monostabilny - KeyBoard('A', 1); - } - else - { - KeyBoard('A', 0); - KeyBoard(0x10, 0); - } - if (maskData[2] & 0x80) if (lastStateData[2] & 0x80) - KeyBoard('A', 1); // syrena niska - else - KeyBoard('A', 0); // monostabilny - - - //PORT3 - - if (maskSwitch[3] & 0x01) - { - if (bitSwitch[3] & 0x01) - { - KeyBoard('J', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('J', 0); - maskSwitch[3] &= ~0x01; - } - if (maskSwitch[3] & 0x02) - { - if (bitSwitch[3] & 0x02) - { - KeyBoard('Y', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('Y', 0); - maskSwitch[3] &= ~0x02; - } - if (maskSwitch[3] & 0x04) - { - if (bitSwitch[3] & 0x04) - { - KeyBoard('U', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('U', 0); - maskSwitch[3] &= ~0x04; - } - if (maskSwitch[3] & 0x08) - { - if (bitSwitch[3] & 0x08) - { - KeyBoard('I', 0); - KeyBoard(0x10, 0); - } - else KeyBoard('I', 0); - maskSwitch[3] &= ~0x08; - } - - - // bateria - if (maskData[3] & 0x01) if (lastStateData[3] & 0x01) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('J', 1); - maskSwitch[3] |= 0x01; - bitSwitch[3] |= 0x01; - } - else - { - maskSwitch[3] |= 0x01; - bitSwitch[3] &= ~0x01; - KeyBoard('J', 1); - } - //Światło lewe - if (maskData[3] & 0x02) if (lastStateData[3] & 0x02) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('Y', 1); - maskSwitch[3] |= 0x02; - bitSwitch[3] |= 0x02; - }else - { - maskSwitch[3] |= 0x02; - bitSwitch[3] &= ~0x02; - KeyBoard('Y', 1); - } - //światło górne - if (maskData[3] & 0x04) if (lastStateData[3] & 0x04) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('U', 1); - maskSwitch[3] |= 0x04; - bitSwitch[3] |= 0x04; - } - else - { - maskSwitch[3] |= 0x04; - bitSwitch[3] &= ~0x04; - KeyBoard('U', 1); - } - //światło prawe - if (maskData[3] & 0x08) if (lastStateData[3] & 0x08) - { - KeyBoard(0x10, 1); // bistabilny - KeyBoard('I', 1); - maskSwitch[3] |= 0x08; - bitSwitch[3] |= 0x08; - } - else - { - maskSwitch[3] |= 0x08; - bitSwitch[3] &= ~0x08; - KeyBoard('I', 1); - } - - - - /* NASTAWNIK, BOCZNIK i KIERUNEK */ - - if (bnkMask & 1) - { // puszczanie klawiszy - KeyBoard(0x6B, 0); - bnkMask &= ~1; - } - if (bnkMask & 2) - { - KeyBoard(0x6D, 0); - bnkMask &= ~2; - } - if (bnkMask & 4) - { - KeyBoard(0x6F, 0); - bnkMask &= ~4; - } - if (bnkMask & 8) - { - KeyBoard(0x6A, 0); - bnkMask &= ~8; - } - - if (nastawnik < ReadDataBuff[6]) - { - bnkMask |= 1; - nastawnik++; - KeyBoard(0x6B, 1); // wciśnij + i dodaj 1 do nastawnika - } - if (nastawnik > ReadDataBuff[6]) - { - bnkMask |= 2; - nastawnik--; - KeyBoard(0x6D, 1); // wciśnij - i odejmij 1 do nastawnika - } - if (bocznik < ReadDataBuff[7]) - { - bnkMask |= 4; - bocznik++; - KeyBoard(0x6F, 1); // wciśnij / i dodaj 1 do bocznika - } - if (bocznik > ReadDataBuff[7]) - { - bnkMask |= 8; - bocznik--; - KeyBoard(0x6A, 1); // wciśnij * i odejmij 1 do bocznika - } - - /* Obsługa HASLERA */ - if (ReadDataBuff[0] & 0x80) - bCzuwak = true; - - if (bKabina1) - { // logika rysika 1 - bRysik1H = true; - bRysik1L = false; - if (bPrzejazdSHP) - bRysik1H = false; - } - else if (bKabina2) - { - bRysik1L = true; - bRysik1H = false; - if (bPrzejazdSHP) - bRysik1L = false; - } - else - { - bRysik1H = false; - bRysik1L = false; - } - - if (bHamowanie) - { // logika rysika 2 - bRysik2H = false; - bRysik2L = true; - } - else - { - if (bCzuwak) - bRysik2H = true; - else - bRysik2H = false; - bRysik2L = false; - } - bCzuwak = false; - if (bRysik1H) - WriteDataBuff[6] |= 1 << 0; - else - WriteDataBuff[6] &= ~(1 << 0); - if (bRysik1L) - WriteDataBuff[6] |= 1 << 1; - else - WriteDataBuff[6] &= ~(1 << 1); - if (bRysik2H) - WriteDataBuff[6] |= 1 << 2; - else - WriteDataBuff[6] &= ~(1 << 2); - if (bRysik2L) - WriteDataBuff[6] |= 1 << 3; - else - WriteDataBuff[6] &= ~(1 << 3); -} - -void TMWDComm::KeyBoard(int key, bool s) // emulacja klawiatury -{ - INPUT ip; - // Set up a generic keyboard event. - ip.type = INPUT_KEYBOARD; - ip.ki.wScan = 0; // hardware scan code for key - ip.ki.time = 0; - ip.ki.dwExtraInfo = 0; - - ip.ki.wVk = key; // virtual-key code for the "a" key - - if (s) - { // Press the "A" key - ip.ki.dwFlags = 0; // 0 for key press - SendInput(1, &ip, sizeof(INPUT)); - } - else - { - ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release - SendInput(1, &ip, sizeof(INPUT)); - } - - // return 1; -} diff --git a/Console/MWD.h b/Console/MWD.h deleted file mode 100644 index 93f2dab4..00000000 --- a/Console/MWD.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -/* - Program obsługi portu COM i innych na potrzeby sterownika MWDevice - (oraz innych wykorzystujących komunikację przez port COM) - dla Symulatora Pojazdów Szynowych MaSzyna - author: Maciej Witek 2016 - Autor nie ponosi odpowiedzialności za niewłaciwe używanie lub działanie programu! -*/ - -#ifndef MWDH -#define MWDH -//--------------------------------------------------------------------------- - -#define BYTETOWRITE 31 // ilość bajtów przesyłanych z MaSzyny -#define BYTETOREAD 16 // ilość bajtów przesyłanych do MaSzyny - -typedef unsigned char BYTE; -typedef unsigned long DWORD; - -class TMWDComm -{ -private: - int MWDTime; // - char lastStateData[6], maskData[6], maskSwitch[6], bitSwitch[6]; - int bocznik, nastawnik, kierunek; - char bnkMask; - - bool ReadData(); //BYTE *pReadDataBuff); - bool SendData(); //BYTE *pWriteDataBuff); - void CheckData(); //sprawdzanie zmian wejść i kontrola mazaków HASLERA - void KeyBoard(int key, bool s); - - //void CheckData2(); - - bool bRysik1H; - bool bRysik1L; - bool bRysik2H; - bool bRysik2L; - -public: - bool Open(); // Otwarcie portu - bool Close(); // Zamknięcie portu - bool Run(); // Obsługa portu - bool GetMWDState(); // sprawdź czy port jest otwarty, 0 zamknięty, 1 otwarty - - // zmienne do rysików HASLERA - bool bSHPstate; - bool bPrzejazdSHP; - bool bKabina1; - bool bKabina2; - bool bHamowanie; - bool bCzuwak; - - unsigned int uiAnalog[4]; // trzymanie danych z wejść analogowych - - BYTE ReadDataBuff[BYTETOREAD+2]; //17]; // bufory danych - BYTE WriteDataBuff[BYTETOWRITE+2]; //31]; - - TMWDComm(); // konstruktor - ~TMWDComm(); // destruktor -}; -#endif - -/* -INFO - wpisy do eu07.ini: - -mwdmasterenable yes // włącz MWD (master MWD Enable) -mwddebugenable yes // włącz logowanie -mwddebugmode 4 // tryb debugowania (które logi) - -mwdcomportname COM3 // nazwa portu -mwdbaudrate 500000 // prędkość transmisji - -mwdinputenable yes // włącz wejścia (przyciski, przełączniki) -mwdbreakenable yes // włącz hamulce (wejścia analogowe) - -mwdmainbreakconfig 0 1023 // konfiguracja kranu zespolonego -> min, max (położenie kranu - odczyt z ADC) -mwdlocbreakconfig 0 1023 // konfiguracja kranu maszynisty -> min, max (położenie kranu - odczyt z ADC) -mwdanalogin2config 0 1023 -mwdanalogin2config 0 1023 - -mwdmaintankpress 0.9 1023 // max ciśnienie w zbiorniku głownym i rozdzielczość -mwdmainpipepress 0.7 1023 // max ciśnienie w przewodzie głównym i rozdzielczość -mwdbreakpress 0.5 1023 // max ciśnienie w cylindrach hamulcowych i rozdzielczość - -mwdhivoltmeter 4000 1023 // max napięcie na woltomierzu WN -mwdhiampmeter 800 1023 // max prąd amperomierza WN - -mwddivider 5 // dzielnik - czym większy tym rzadziej czyta diwajs -*/ diff --git a/Driver.cpp b/Driver.cpp index b3e10325..1e9533c0 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1422,11 +1422,15 @@ void TController::TablePurger() iLast = sSpeedTable.size() - 1; }; -void TController::TableSort() -{ +void TController::TableSort() { + + if( sSpeedTable.size() < 3 ) { + // we skip last slot and no point in checking if there's only one other entry + return; + } TSpeedPos sp_temp = TSpeedPos(); // uzywany do przenoszenia - for (std::size_t i = 0; i < (iLast - 1) && iLast > 0; ++i) - { // pętla tylko do dwóch pozycji od końca bo ostatniej nie modyfikujemy + for( std::size_t i = 0; i < ( iLast - 1 ); ++i ) { + // pętla tylko do dwóch pozycji od końca bo ostatniej nie modyfikujemy if (sSpeedTable[i].fDist > sSpeedTable[i + 1].fDist) { // jesli pozycja wcześniejsza jest dalej to źle sp_temp = sSpeedTable[i + 1]; @@ -4574,24 +4578,52 @@ TController::UpdateSituation(double dt) { } // koniec predkosci nastepnej - if( vel > VelDesired ) { - // jesli jedzie za szybko do AKTUALNEGO - if( VelDesired == 0.0 ) { - // jesli stoj, to hamuj, ale i tak juz za pozno :) - AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie - } - else { - // try to estimate increase of current velocity before engaged brakes start working - // if it looks like we'll exceed maximum allowed speed start thinking about slight slowing down - if( ( vel + vel * ( 1.0 - fBrake_a0[ 0 ] ) * AbsAccS ) > ( VelDesired + fVelPlus ) ) { - // hamuj tak średnio - AccDesired = std::min( AccDesired, -0.25 ); + // decisions based on current speed + if( mvOccupied->CategoryFlag == 1 ) { + // try to estimate increase of current velocity before engaged brakes start working + auto const speedestimate = vel + vel * ( 1.0 - fBrake_a0[ 0 ] ) * AbsAccS; + if( speedestimate > VelDesired ) { + // jesli jedzie za szybko do AKTUALNEGO + if( VelDesired == 0.0 ) { + // jesli stoj, to hamuj, ale i tak juz za pozno :) + AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie } else { - // o 5 km/h to olej (zacznij luzować) - AccDesired = std::min( - AccDesired, // but don't override decceleration for VelNext - std::max( 0.0, AccPreferred ) ); + if( speedestimate > ( VelDesired + fVelPlus ) ) { + // if it looks like we'll exceed maximum allowed speed start thinking about slight slowing down + AccDesired = std::min( AccDesired, -0.25 ); + } + else { + // close enough to target to stop accelerating + AccDesired = std::min( + AccDesired, // but don't override decceleration for VelNext + interpolate( // ease off as you close to the target velocity + -0.06, AccPreferred, + clamp( speedestimate - vel, 0.0, fVelPlus ) / fVelPlus ) ); + } + } + } + } + else { + // for cars the older version works better + if( vel > VelDesired ) { + // jesli jedzie za szybko do AKTUALNEGO + if( VelDesired == 0.0 ) { + // jesli stoj, to hamuj, ale i tak juz za pozno :) + AccDesired = std::min( AccDesired, -0.9 ); // hamuj solidnie + } + else { + // slow down, not full stop + if( vel > ( VelDesired + fVelPlus ) ) { + // hamuj tak średnio + AccDesired = std::min( AccDesired, -fBrake_a0[ 0 ] * 0.5 ); + } + else { + // o 5 km/h to olej (zacznij luzować) + AccDesired = std::min( + AccDesired, // but don't override decceleration for VelNext + std::max( 0.0, AccPreferred ) ); + } } } } @@ -4599,8 +4631,10 @@ TController::UpdateSituation(double dt) { // last step sanity check, until the whole calculation is straightened out AccDesired = std::min( AccDesired, AccPreferred ); - // also take into account impact of gravity - AccDesired = clamp( AccDesired - fAccGravity, -0.9, 0.9 ); + if( mvOccupied->CategoryFlag == 1 ) { + // also take into account impact of gravity + AccDesired = clamp( AccDesired - fAccGravity, -0.9, 0.9 ); + } diff --git a/Driver.h b/Driver.h index fe284162..2fe884fc 100644 --- a/Driver.h +++ b/Driver.h @@ -171,8 +171,8 @@ extern bool WriteLogFlag; // logowanie parametrów fizycznych static const int BrakeAccTableSize = 20; //---------------------------------------------------------------------------- -class TController -{ +class TController { + private: // obsługa tabelki prędkości (musi mieć możliwość odhaczania stacji w rozkładzie) int iLast{ 0 }; // ostatnia wypełniona pozycja w tabeli uart; } void screenshot_save_thread( char *img ) @@ -354,9 +355,13 @@ int main(int argc, char *argv[]) sound_man = std::make_unique(); + Global::pWorld = &World; + input::Keyboard.init(); input::Mouse.init(); input::Gamepad.init(); + if (Global::uart_conf.enable) + input::uart = std::make_unique(); Global::pWorld = &World; if( false == World.Init( window ) ) { @@ -398,6 +403,8 @@ int main(int argc, char *argv[]) && ( true == GfxRenderer.Render() ) ) { glfwPollEvents(); input::Keyboard.poll(); + if (input::uart) + input::uart->poll(); if( true == Global::InputMouse ) { input::Mouse.poll(); } if( true == Global::InputGamepad ) { input::Gamepad.poll(); } } diff --git a/Float3d.h b/Float3d.h index 0216b000..7efe6ba3 100644 --- a/Float3d.h +++ b/Float3d.h @@ -25,6 +25,11 @@ class float3 }; float Length() const; float LengthSquared() const; + + operator glm::vec3() const + { + return glm::vec3(x, y, z); + } }; inline bool operator==(const float3 &v1, const float3 &v2) diff --git a/Gauge.cpp b/Gauge.cpp index 0b9c427d..74f255e0 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -19,6 +19,8 @@ http://mozilla.org/MPL/2.0/. #include "Model3d.h" #include "Timer.h" #include "Logs.h" +#include "World.h" +#include "Train.h" void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, double fNewOffset, double fNewFriction, double fNewValue) { // ustawienie parametrów animacji submodelu @@ -315,14 +317,33 @@ void TGauge::UpdateValue() } }; -void -TGauge::play( sound *Sound ) { +// todo: ugly approach to getting train translation +// maybe cache gauge position - if( Sound == nullptr ) { return; } +extern TWorld World; + +void TGauge::play( sound *Sound ) +{ + if (!Sound) + return; Sound->stop(); + + if (SubModel && World.train()) + { + float4x4 mat; + SubModel->ParentMatrix(&mat); + glm::vec3 pos = *mat.TranslationGet(); + + if (glm::length(pos) > 1.0f) + { + pos = glm::vec3(glm::vec4(pos, 1.0f) * glm::inverse((glm::mat4)World.train()->Dynamic()->mMatrix)); + pos = pos + (glm::vec3)World.train()->Dynamic()->GetPosition(); + + Sound->set_mode(sound::anchored).dist(1.5f).position(pos); + } + } + Sound->play(); return; } - -//--------------------------------------------------------------------------- diff --git a/Gauge.h b/Gauge.h index 02ed3ee0..8dd61959 100644 --- a/Gauge.h +++ b/Gauge.h @@ -68,7 +68,7 @@ class TGauge { void AssignDouble(double *dValue); void AssignInt(int *iValue); void UpdateValue(); - TSubModel *SubModel; // McZapkie-310302: zeby mozna bylo sprawdzac czy zainicjowany poprawnie + TSubModel *SubModel = nullptr; // McZapkie-310302: zeby mozna bylo sprawdzac czy zainicjowany poprawnie }; //--------------------------------------------------------------------------- diff --git a/Globals.cpp b/Globals.cpp index a8de9723..9621a627 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -166,24 +166,11 @@ int Global::iPoKeysPWM[7] = {0, 1, 2, 3, 4, 5, 6}; bool Global::bnewAirCouplers = true; double Global::fTimeSpeed = 1.0; // przyspieszenie czasu, zmienna do testów bool Global::bHideConsole = false; // hunter-271211: ukrywanie konsoli + +Global::uart_conf_t Global::uart_conf; + //randomizacja std::mt19937 Global::random_engine = std::mt19937(std::time(NULL)); -// maciek001: konfiguracja wstępna portu COM -bool Global::bMWDmasterEnable = false; // główne włączenie portu! -bool Global::bMWDdebugEnable = false; // włącz dodawanie do logu -int Global::iMWDDebugMode = 0; // co ma wyświetlać w logu -std::string Global::sMWDPortId = "COM1"; // nazwa portu z którego korzystamy -unsigned long int Global::iMWDBaudrate = 9600; // prędkość transmisji danych -bool Global::bMWDInputEnable = false; // włącz wejścia -bool Global::bMWDBreakEnable = false; // włącz wejścia analogowe -double Global::fMWDAnalogInCalib[4][2] = { { 0, 1023 },{ 0, 1023 },{ 0, 1023 },{ 0, 1023 } }; // wartość max potencjometru, wartość min potencjometru, rozdzielczość (max. wartość jaka może być) -double Global::fMWDzg[2] = { 0.9, 1023 }; -double Global::fMWDpg[2] = { 0.8, 1023 }; -double Global::fMWDph[2] = { 0.6, 1023 }; -double Global::fMWDvolt[2] = { 4000, 1023 }; -double Global::fMWDamp[2] = { 800, 1023 }; -double Global::fMWDlowVolt[2] = { 150, 1023 }; -int Global::iMWDdivider = 5; opengl_light Global::DayLight; Global::soundmode_t Global::soundpitchmode = Global::linear; @@ -756,102 +743,33 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::InputGamepad; } - // maciek001: ustawienia MWD - else if (token == "mwdmasterenable") { // główne włączenie maszyny! - Parser.getTokens(); - Parser >> bMWDmasterEnable; - if (bMWDdebugEnable) WriteLog("SerialPort Master Enable"); - } - else if (token == "mwddebugenable") { // logowanie pracy - Parser.getTokens(); - Parser >> bMWDdebugEnable; - if (bMWDdebugEnable) WriteLog("MWD Debug Mode On"); - } - else if (token == "mwddebugmode") { // co ma być debugowane? - Parser.getTokens(1, false); - Parser >> iMWDDebugMode; - if (bMWDdebugEnable) WriteLog("Debug Mode = " + to_string(iMWDDebugMode)); - } - else if (token == "mwdcomportname") { // nazwa portu COM - Parser.getTokens(); - Parser >> sMWDPortId; - if (bMWDdebugEnable) WriteLog("PortName " + sMWDPortId); - } - else if (token == "mwdbaudrate") { // prędkość transmisji danych - Parser.getTokens(1, false); - Parser >> iMWDBaudrate; - if (bMWDdebugEnable) WriteLog("Baud rate = " + to_string((int)(iMWDBaudrate / 1000)) + (" kbps")); - } - else if (token == "mwdinputenable") { // włącz wejścia - Parser.getTokens(); - Parser >> bMWDInputEnable; - if (bMWDdebugEnable && bMWDInputEnable) WriteLog("MWD Input Enable"); - } - else if (token == "mwdbreakenable") { // włącz obsługę hamulców - Parser.getTokens(); - Parser >> bMWDBreakEnable; - if (bMWDdebugEnable && bMWDBreakEnable) WriteLog("MWD Break Enable"); - } - else if (token == "mwdmainbreakconfig") { // ustawienia hamulca zespolonego - Parser.getTokens(2, false); - Parser >> fMWDAnalogInCalib[0][0] >> fMWDAnalogInCalib[0][1]; - if (bMWDdebugEnable) WriteLog("Main break settings: " + to_string(fMWDAnalogInCalib[0][0]) + (" ") + to_string(fMWDAnalogInCalib[0][1])); - } - else if (token == "mwdlocbreakconfig") { // ustawienia hamulca lokomotywy - Parser.getTokens(2, false); - Parser >> fMWDAnalogInCalib[1][0] >> fMWDAnalogInCalib[1][1]; - if (bMWDdebugEnable) WriteLog("Locomotive break settings: " + to_string(fMWDAnalogInCalib[1][0]) + (" ") + to_string(fMWDAnalogInCalib[1][1])); - } - else if (token == "mwdanalogin1config") { // ustawienia hamulca zespolonego - Parser.getTokens(2, false); - Parser >> fMWDAnalogInCalib[2][0] >> fMWDAnalogInCalib[2][1]; - if (bMWDdebugEnable) WriteLog("Analog input 1 settings: " + to_string(fMWDAnalogInCalib[2][0]) + (" ") + to_string(fMWDAnalogInCalib[2][1])); - } - else if (token == "mwdanalogin2config") { // ustawienia hamulca lokomotywy - Parser.getTokens(2, false); - Parser >> fMWDAnalogInCalib[3][0] >> fMWDAnalogInCalib[3][1]; - if (bMWDdebugEnable) WriteLog("Analog input 2 settings: " + to_string(fMWDAnalogInCalib[3][0]) + (" ") + to_string(fMWDAnalogInCalib[3][1])); - } - else if (token == "mwdmaintankpress") { // max ciśnienie w zbiorniku głownym i rozdzielczość - Parser.getTokens(2, false); - Parser >> fMWDzg[0] >> fMWDzg[1]; - if (bMWDdebugEnable) WriteLog("MainAirTank settings: " + to_string(fMWDzg[0]) + (" ") + to_string(fMWDzg[1])); - } - else if (token == "mwdmainpipepress") { // max ciśnienie w przewodzie głownym i rozdzielczość - Parser.getTokens(2, false); - Parser >> fMWDpg[0] >> fMWDpg[1]; - if (bMWDdebugEnable) WriteLog("MainAirPipe settings: " + to_string(fMWDpg[0]) + (" ") + to_string(fMWDpg[1])); - } - else if (token == "mwdbreakpress") { // max ciśnienie w hamulcach i rozdzielczość - Parser.getTokens(2, false); - Parser >> fMWDph[0] >> fMWDph[1]; - if (bMWDdebugEnable) WriteLog("AirPipe settings: " + to_string(fMWDph[0]) + (" ") + to_string(fMWDph[1])); - } - else if (token == "mwdhivoltmeter") { // max napięcie na woltomierzu WN - Parser.getTokens(2, false); - Parser >> fMWDvolt[0] >> fMWDvolt[1]; - if (bMWDdebugEnable) WriteLog("VoltMeter settings: " + to_string(fMWDvolt[0]) + (" ") + to_string(fMWDvolt[1])); - } - else if (token == "mwdhiampmeter") { - Parser.getTokens(2, false); - Parser >> fMWDamp[0] >> fMWDamp[1]; - if (bMWDdebugEnable) WriteLog("Amp settings: " + to_string(fMWDamp[0]) + (" ") + to_string(fMWDamp[1])); - } - else if (token == "mwdlowvoltmeter") { - Parser.getTokens(2, false); - Parser >> fMWDlowVolt[0] >> fMWDlowVolt[1]; - if (bMWDdebugEnable) WriteLog("Low VoltMeter settings: " + to_string(fMWDlowVolt[0]) + (" ") + to_string(fMWDlowVolt[1])); - } - else if (token == "mwddivider") { - Parser.getTokens(1, false); - Parser >> iMWDdivider; - if (iMWDdivider == 0) - { - WriteLog("Dzielnik nie może być równy ZERO! Ustawiam na 1!"); - iMWDdivider = 1; - } - if (bMWDdebugEnable) WriteLog("Divider = " + to_string(iMWDdivider)); - } + else if (token == "uart") + { + Parser.getTokens(3, false); + Global::uart_conf.enable = true; + Parser >> Global::uart_conf.port; + Parser >> Global::uart_conf.baud; + Parser >> Global::uart_conf.interval; + Parser >> Global::uart_conf.updatetime; + } + else if (token == "uarttune") + { + Parser.getTokens(14); + Parser >> Global::uart_conf.mainbrakemin + >> Global::uart_conf.mainbrakemax + >> Global::uart_conf.localbrakemin + >> Global::uart_conf.localbrakemax + >> Global::uart_conf.tankmax + >> Global::uart_conf.tankuart + >> Global::uart_conf.pipemax + >> Global::uart_conf.pipeuart + >> Global::uart_conf.brakemax + >> Global::uart_conf.brakeuart + >> Global::uart_conf.hvmax + >> Global::uart_conf.hvuart + >> Global::uart_conf.currentmax + >> Global::uart_conf.currentuart; + } } while ((token != "") && (token != "endconfig")); //(!Parser->EndOfFile) // na koniec trochę zależności if (!bLoadTraction) // wczytywanie drutów i słupów diff --git a/Globals.h b/Globals.h index 92013019..8848ffc6 100644 --- a/Globals.h +++ b/Globals.h @@ -293,6 +293,31 @@ class Global //randomizacja static std::mt19937 random_engine; + struct uart_conf_t + { + bool enable = false; + std::string port; + int baud; + int interval; + float updatetime; + + float mainbrakemin = 0.0f; + float mainbrakemax = 65535.0f; + float localbrakemin = 0.0f; + float localbrakemax = 65535.0f; + float tankmax = 10.0f; + float tankuart = 65535.0f; + float pipemax = 10.0f; + float pipeuart = 65535.0f; + float brakemax = 10.0f; + float brakeuart = 65535.0f; + float hvmax = 100000.0f; + float hvuart = 65535.0f; + float currentmax = 10000.0f; + float currentuart = 65535.0f; + }; + static uart_conf_t uart_conf; + // metody static void TrainDelete(TDynamicObject *d); static void ConfigParse(cParser &parser); @@ -303,23 +328,6 @@ class Global static std::string Bezogonkow(std::string str, bool _ = false); static double Min0RSpeed(double vel1, double vel2); - // maciek001: zmienne dla MWD - static bool bMWDmasterEnable; // główne włączenie portu COM - static bool bMWDdebugEnable; // logowanie pracy - static int iMWDDebugMode; - static std::string sMWDPortId; // nazwa portu COM - static unsigned long int iMWDBaudrate; // prędkość transmisji - static bool bMWDInputEnable; // włącz wejścia - static bool bMWDBreakEnable; // włącz wejścia analogowe (hamulce) - static double fMWDAnalogInCalib[4][2]; // ustawienia kranów hamulca zespolonego i dodatkowego - min i max - static double fMWDzg[2]; // max wartość wskazywana i max wartość generowana (rozdzielczość) - static double fMWDpg[2]; - static double fMWDph[2]; - static double fMWDvolt[2]; - static double fMWDamp[2]; - static double fMWDlowVolt[2]; - static int iMWDdivider; - static opengl_light DayLight; enum soundmode_t diff --git a/Ground.cpp b/Ground.cpp index 32385916..8ab0cb70 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -3593,6 +3593,45 @@ TGround::Update_Lights() { m_lights.update(); } +void +TGround::Update_Hidden() { + + // rednerowanie globalnych (nie za często?) + for( TGroundNode *node = srGlobal.nRenderHidden; node; node = node->nNext3 ) { + node->RenderHidden(); + } + + // render events and sounds from sectors near enough to the viewer + auto const range = 2750.0; // audible range of 100 db sound + int const camerax = static_cast( std::floor( Global::pCameraPosition.x / 1000.0 ) + iNumRects / 2 ); + int const cameraz = static_cast( std::floor( Global::pCameraPosition.z / 1000.0 ) + iNumRects / 2 ); + int const segmentcount = 2 * static_cast( std::ceil( range / 1000.0 ) ); + int const originx = std::max( 0, camerax - segmentcount / 2 ); + int const originz = std::max( 0, cameraz - segmentcount / 2 ); + + for( int column = originx; column <= originx + segmentcount; ++column ) { + for( int row = originz; row <= originz + segmentcount; ++row ) { + + auto &cell = Rects[ column ][ row ]; + + for( int subcellcolumn = 0; subcellcolumn < iNumSubRects; ++subcellcolumn ) { + for( int subcellrow = 0; subcellrow < iNumSubRects; ++subcellrow ) { + auto subcell = cell.FastGetSubRect( subcellcolumn, subcellrow ); + if( subcell == nullptr ) { continue; } + // renderowanie obiektów aktywnych a niewidocznych + for( auto node = subcell->nRenderHidden; node; node = node->nNext3 ) { + node->RenderHidden(); + } + // jeszcze dźwięki pojazdów by się przydały, również niewidocznych + // TODO: move to sound renderer + subcell->RenderSounds(); + } + } + } + } + +} + // Winger 170204 - szukanie trakcji nad pantografami bool TGround::GetTraction(TDynamicObject *model) { // aktualizacja drutu zasilającego dla każdego pantografu, żeby odczytać napięcie diff --git a/Ground.h b/Ground.h index 56c316d9..2dc5cc1a 100644 --- a/Ground.h +++ b/Ground.h @@ -228,7 +228,7 @@ public: return pSubRects + iRow * iNumSubRects + iCol; // zwrócenie właściwego }; // pobranie wskaźnika do małego kwadratu, bez tworzenia jeśli nie ma - TSubRect * FastGetSubRect(int iCol, int iRow) { + TSubRect *FastGetSubRect(int iCol, int iRow) const { return ( pSubRects ? pSubRects + iRow * iNumSubRects + iCol : @@ -289,6 +289,7 @@ class TGround void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS void Update_Lights(); // updates scene lights array + void Update_Hidden(); // updates invisible elements of the scene bool AddToQuery(TEvent *Event, TDynamicObject *Node); bool GetTraction(TDynamicObject *model); bool CheckQuery(); diff --git a/Model3d.cpp b/Model3d.cpp index 60d5b6f0..a16c7512 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1090,7 +1090,9 @@ void TSubModel::ParentMatrix(float4x4 *m) // jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D), to // dodatkowy obrót // obrót T3D jest wymagany np. do policzenia wysokości pantografów - *m = float4x4(*fMatrix); // skopiowanie, bo będziemy mnożyć + m->Identity(); + if (fMatrix) + *m = float4x4(*fMatrix); // skopiowanie, bo będziemy mnożyć // m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) TSubModel *sm = this; while (sm->Parent) diff --git a/PyInt.cpp b/PyInt.cpp index c73fdeab..def8b8ef 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -479,6 +479,7 @@ void TPythonScreens::update() void TPythonScreens::setLookupPath(std::string const &path) { _lookupPath = path; + std::replace(_lookupPath.begin(), _lookupPath.end(), '\\', '/'); } TPythonScreens::TPythonScreens() diff --git a/Train.cpp b/Train.cpp index 585466a5..9800f0bc 100644 --- a/Train.cpp +++ b/Train.cpp @@ -824,8 +824,7 @@ void TTrain::OnCommand_trainbrakedecrease( TTrain *Train, command_data const &Co if( ( Train->mvOccupied->BrakeCtrlPos == -1 ) && ( Train->mvOccupied->BrakeHandle == FVel6 ) && ( Train->DynamicObject->Controller != AIdriver ) - && ( Global::iFeedbackMode != 4 ) - && ( !( Global::bMWDmasterEnable && Global::bMWDBreakEnable ) ) ) { + && ( Global::iFeedbackMode != 4 ) ) { // Odskakiwanie hamulce EP Train->mvOccupied->BrakeLevelSet( Train->mvOccupied->BrakeCtrlPos + 1 ); Train->keybrakecount = 0; @@ -856,8 +855,7 @@ void TTrain::OnCommand_trainbrakecharging( TTrain *Train, command_data const &Co if( ( Train->mvOccupied->BrakeCtrlPos == -1 ) && ( Train->mvOccupied->BrakeHandle == FVel6 ) && ( Train->DynamicObject->Controller != AIdriver ) - && ( Global::iFeedbackMode != 4 ) - && ( !( Global::bMWDmasterEnable && Global::bMWDBreakEnable ) ) ) { + && ( Global::iFeedbackMode != 4 ) ) { // Odskakiwanie hamulce EP Train->mvOccupied->BrakeLevelSet( Train->mvOccupied->BrakeCtrlPos + 1 ); Train->keybrakecount = 0; @@ -1248,7 +1246,12 @@ void TTrain::OnCommand_alerteracknowledge( TTrain *Train, command_data const &Co } } -void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command ) { +void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command ) +{ + if (Command.desired_state == command_data::ON && Train->mvOccupied->Battery) + return; + if (Command.desired_state == command_data::OFF && !Train->mvOccupied->Battery) + return; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down @@ -1289,7 +1292,19 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command } } -void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const &Command ) { +void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const &cmd ) +{ + command_data Command = cmd; + + if (Train->mvOccupied->PantSwitchType != "impulse") + { + if (Command.desired_state == Command.ON && Train->mvControlled->PantFrontUp) + return; + if (Command.desired_state == Command.OFF && !Train->mvControlled->PantFrontUp) + return; + } + else if (Command.desired_state == Command.OFF) + Command.action = GLFW_RELEASE; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down @@ -1369,7 +1384,18 @@ void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const } } -void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const &Command ) { +void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const &cmd ) { + command_data Command = cmd; + + if (Train->mvOccupied->PantSwitchType != "impulse") + { + if (Command.desired_state == Command.ON && Train->mvControlled->PantRearUp) + return; + if (Command.desired_state == Command.OFF && !Train->mvControlled->PantRearUp) + return; + } + else if (Command.desired_state == Command.OFF) + Command.action = GLFW_RELEASE; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down @@ -1562,7 +1588,8 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com if( Command.action != GLFW_RELEASE ) { // press or hold... - if( Train->m_linebreakerstate == 0 ) { + if ((Command.desired_state == command_data::TOGGLE && Train->m_linebreakerstate == 0) + || Command.desired_state == command_data::ON) { // ...to close the circuit if( Command.action == GLFW_PRESS ) { // fresh press, start fresh closing delay calculation @@ -1609,39 +1636,26 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com } } } - else if( Train->m_linebreakerstate == 1 ) { + else if ((Command.desired_state == command_data::TOGGLE && Train->m_linebreakerstate == 1) + || Command.desired_state == command_data::OFF) { // ...to open the circuit - if( true == Train->mvControlled->MainSwitch( false ) ) { + Train->mvControlled->MainSwitch( false ); - Train->m_linebreakerstate = -1; + Train->m_linebreakerstate = -1; - if( Train->ggMainOffButton.SubModel != nullptr ) { - // two separate switches to close and break the circuit + if( Train->ggMainOffButton.SubModel != nullptr ) { + // two separate switches to close and break the circuit + // visual feedback + Train->ggMainOffButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else if( Train->ggMainButton.SubModel != nullptr ) { + // single two-state switch + { // visual feedback - Train->ggMainOffButton.UpdateValue( 1.0, Train->dsbSwitch ); - } - else if( Train->ggMainButton.SubModel != nullptr ) { - // single two-state switch -/* - // NOTE: we don't have switch type definition for the line breaker switch - // so for the time being we have hard coded "impulse" switches for all EMUs - // TODO: have proper switch type config for all switches, and put it in the cab switch descriptions, not in the .fiz - if( Train->mvControlled->TrainType == dt_EZT ) { - // audio feedback - if( Command.action == GLFW_PRESS ) { - Train->play_sound( Train->dsbSwitch ); - } - // visual feedback - Train->ggMainButton.UpdateValue( 1.0 ); - } - else -*/ - { - // visual feedback - Train->ggMainButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->ggMainButton.UpdateValue( 0.0, Train->dsbSwitch ); } } + // play sound immediately when the switch is hit, not after release if( Train->fMainRelayTimer > 0.0f ) { Train->play_sound( Train->dsbRelay ); @@ -1713,6 +1727,11 @@ void TTrain::OnCommand_convertertoggle( TTrain *Train, command_data const &Comma return; } + if (Train->mvControlled->ConverterAllow && Command.desired_state == command_data::ON) + return; + if (!Train->mvControlled->ConverterAllow && Command.desired_state == command_data::OFF) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( ( false == Train->mvControlled->ConverterAllow ) @@ -1866,6 +1885,11 @@ void TTrain::OnCommand_compressortoggle( TTrain *Train, command_data const &Comm return; } + if (Train->mvControlled->CompressorAllow && Command.desired_state == command_data::ON) + return; + if (!Train->mvControlled->CompressorAllow && Command.desired_state == command_data::OFF) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->CompressorAllow ) { @@ -1945,8 +1969,8 @@ void TTrain::OnCommand_compressortogglelocal( TTrain *Train, command_data const } } -void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &Command ) { - +void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &cmd ) { + command_data Command = cmd; // TODO: don't rely on presense of 3d model to determine presence of the switch if( Train->ggStLinOffButton.SubModel == nullptr ) { if( Command.action == GLFW_PRESS ) { @@ -1954,6 +1978,17 @@ void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &C } return; } + + if (Train->mvControlled->StLinSwitchType == "toggle") + { + if (Command.desired_state == Command.ON && Train->mvControlled->StLinSwitchOff) + return; + if (Command.desired_state == Command.OFF && !Train->mvControlled->StLinSwitchOff) + return; + } + else if (Command.desired_state == Command.OFF) + Command.action = GLFW_RELEASE; + // NOTE: because we don't have modeled actual circuits this is a simplification of the real mechanics // namely, pressing the button will flip it in the entire unit, which isn't exactly physically possible if( Command.action == GLFW_PRESS ) { @@ -2064,7 +2099,12 @@ void TTrain::OnCommand_motordisconnect( TTrain *Train, command_data const &Comma } } -void TTrain::OnCommand_motoroverloadrelaythresholdtoggle( TTrain *Train, command_data const &Command ) { +void TTrain::OnCommand_motoroverloadrelaythresholdtoggle( TTrain *Train, command_data const &Command ) +{ + if (Train->mvControlled->Imax < Train->mvControlled->ImaxHi && Command.desired_state == command_data::OFF) + return; + if (!(Train->mvControlled->Imax < Train->mvControlled->ImaxHi) && Command.desired_state == command_data::ON) + return; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down @@ -2119,9 +2159,16 @@ void TTrain::OnCommand_headlighttoggleleft( TTrain *Train, command_data const &C 0 : 1 ); + bool current_state = (bool)(Train->DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::headlight_left); + + if (Command.desired_state == command_data::ON && current_state) + return; + if (Command.desired_state == command_data::OFF && !current_state) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( ( Train->DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::headlight_left ) == 0 ) { + if( !current_state ) { // turn on Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_left; // visual feedback @@ -2152,9 +2199,16 @@ void TTrain::OnCommand_headlighttoggleright( TTrain *Train, command_data const & 0 : 1 ); + bool current_state = (bool)(Train->DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::headlight_right); + + if (Command.desired_state == command_data::ON && current_state) + return; + if (Command.desired_state == command_data::OFF && !current_state) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( ( Train->DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::headlight_right ) == 0 ) { + if( !current_state ) { // turn on Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_right; // visual feedback @@ -2185,9 +2239,16 @@ void TTrain::OnCommand_headlighttoggleupper( TTrain *Train, command_data const & 0 : 1 ); + bool current_state = (bool)(Train->DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::headlight_upper); + + if (Command.desired_state == command_data::ON && current_state) + return; + if (Command.desired_state == command_data::OFF && !current_state) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( ( Train->DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::headlight_upper ) == 0 ) { + if( !current_state ) { // turn on Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_upper; // visual feedback @@ -2452,6 +2513,11 @@ void TTrain::OnCommand_headlightsdimtoggle( TTrain *Train, command_data const &C return; } + if (Train->DynamicObject->DimHeadlights && Command.desired_state == command_data::ON) + return; + if (!Train->DynamicObject->DimHeadlights && Command.desired_state == command_data::OFF) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->DynamicObject->DimHeadlights ) { @@ -2509,6 +2575,11 @@ void TTrain::OnCommand_interiorlightdimtoggle( TTrain *Train, command_data const return; } + if (Train->bCabLightDim && Command.desired_state == command_data::ON) + return; + if (!Train->bCabLightDim && Command.desired_state == command_data::OFF) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->bCabLightDim ) { @@ -2564,6 +2635,11 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command return; } + if (Train->mvControlled->Heating && Command.desired_state == command_data::ON) + return; + if (!Train->mvControlled->Heating && Command.desired_state == command_data::OFF) + return; + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->Heating ) { @@ -3719,20 +3795,6 @@ bool TTrain::Update( double const Deltatime ) /// skakanie zapewnia mechanika /// napędu } - - if (Global::bMWDmasterEnable) // pobieranie danych dla pulpitu port (COM) - { - Console::ValueSet(0, mvOccupied->Compressor); // zbiornik główny - Console::ValueSet(1, mvOccupied->PipePress); // przewód główny - Console::ValueSet(2, mvOccupied->BrakePress); // cylinder hamulcowy - Console::ValueSet(3, fHVoltage); // woltomierz wysokiego napięcia - Console::ValueSet(4, fHCurrent[(mvControlled->TrainType & dt_EZT) ? 0 : 1]); - // pierwszy amperomierz; dla EZT prąd całkowity - Console::ValueSet(5, fHCurrent[2]); // drugi amperomierz 2 - Console::ValueSet(6, fHCurrent[3]); // drugi amperomierz 3 - Console::ValueSet(7, fTachoVelocity); - //Console::ValueSet(8, mvControlled->BatteryVoltage); // jeszcze nie pora ;) - } #endif // hunter-080812: wyrzucanie szybkiego na elektrykach gdy nie ma napiecia @@ -4654,7 +4716,7 @@ bool TTrain::Update( double const Deltatime ) #ifdef _WIN32 if (DynamicObject->Mechanik ? (DynamicObject->Mechanik->AIControllFlag ? false : - (Global::iFeedbackMode == 4 || (Global::bMWDmasterEnable && Global::bMWDBreakEnable))) : + (Global::iFeedbackMode == 4)) : false) // nie blokujemy AI { // Ra: nie najlepsze miejsce, ale na początek gdzieś to dać trzeba // Firleju: dlatego kasujemy i zastepujemy funkcją w Console @@ -4663,7 +4725,6 @@ bool TTrain::Update( double const Deltatime ) double b = Console::AnalogCalibrateGet(0); b = b * 8.0 - 2.0; b = clamp( b, -2.0, mvOccupied->BrakeCtrlPosNo ); // przycięcie zmiennej do granic - if (Global::bMWDdebugEnable && Global::iMWDDebugMode & 4) WriteLog("FV4a break position = " + to_string(b)); ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); } @@ -4672,7 +4733,6 @@ bool TTrain::Update( double const Deltatime ) double b = Console::AnalogCalibrateGet(0); b = b * 7.0 - 1.0; b = clamp( b, -1.0, mvOccupied->BrakeCtrlPosNo ); // przycięcie zmiennej do granic - if (Global::bMWDdebugEnable && Global::iMWDDebugMode & 4) WriteLog("FVel6 break position = " + to_string(b)); ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); } @@ -4690,8 +4750,7 @@ bool TTrain::Update( double const Deltatime ) if( ( DynamicObject->Mechanik != nullptr ) && ( false == DynamicObject->Mechanik->AIControllFlag ) // nie blokujemy AI && ( mvOccupied->BrakeLocHandle == FD1 ) - && ( ( Global::iFeedbackMode == 4 ) - || ( Global::bMWDmasterEnable && Global::bMWDBreakEnable ) ) ) { + && ( ( Global::iFeedbackMode == 4 ) ) ) { // Ra: nie najlepsze miejsce, ale na początek gdzieś to dać trzeba // Firleju: dlatego kasujemy i zastepujemy funkcją w Console auto const b = clamp( @@ -4700,10 +4759,6 @@ bool TTrain::Update( double const Deltatime ) ManualBrakePosNo ); ggLocalBrake.UpdateValue( b ); // przesów bez zaokrąglenia mvOccupied->LocalBrakePos = int( 1.09 * b ); // sposób zaokrąglania jest do ustalenia - if( ( true == Global::bMWDdebugEnable ) - && ( ( Global::iMWDDebugMode & 4 ) != 0 ) ) { - WriteLog( "FD1 break position = " + to_string( b ) ); - } } else // standardowa prodedura z kranem powiązanym z klawiaturą #endif @@ -7419,3 +7474,75 @@ TTrain::play_sound( sound* Sound, sound* Fallbacksound, float gain ) { play_sound( Fallbacksound, gain ); } +float TTrain::get_tacho() +{ + return fTachoVelocity; +} + +float TTrain::get_tank_pressure() +{ + return mvOccupied->Compressor; +} + +float TTrain::get_pipe_pressure() +{ + return mvOccupied->PipePress; +} + +float TTrain::get_brake_pressure() +{ + return mvOccupied->BrakePress; +} + +float TTrain::get_hv_voltage() +{ + return fHVoltage; +} + +std::array TTrain::get_current() +{ + return { fHCurrent[(mvControlled->TrainType & dt_EZT) ? 0 : 1], fHCurrent[2], fHCurrent[3] }; +} + +bool TTrain::get_alarm() +{ + return (TestFlag(mvOccupied->SecuritySystem.Status, s_CAalarm) || + TestFlag(mvOccupied->SecuritySystem.Status, s_SHPalarm)); +} + +void TTrain::set_mainctrl(int pos) +{ + if (pos < mvControlled->MainCtrlPos) + mvControlled->DecMainCtrl(1); + else if (pos > mvControlled->MainCtrlPos) + mvControlled->IncMainCtrl(1); +} + +void TTrain::set_scndctrl(int pos) +{ + if (pos < mvControlled->ScndCtrlPos) + mvControlled->DecScndCtrl(1); + else if (pos > mvControlled->ScndCtrlPos) + mvControlled->IncScndCtrl(1); +} + +void TTrain::set_trainbrake(float val) +{ + val = std::min(1.0f, std::max(0.0f, val)); + float min = mvControlled->Handle->GetPos(bh_MIN); + float max = mvControlled->Handle->GetPos(bh_MAX); + mvControlled->BrakeLevelSet(min + val * (max - min)); +} + +void TTrain::set_localbrake(float val) +{ + val = std::min(1.0f, std::max(0.0f, val)); + float min = 0.0f; + float max = (float)LocalBrakePosNo; + mvControlled->LocalBrakePos = std::round(min + val * (max - min)); +} + +int TTrain::get_drive_direction() +{ + return mvOccupied->ActiveDir * mvOccupied->CabNo; +} diff --git a/Train.h b/Train.h index dc05a1e8..194937d6 100644 --- a/Train.h +++ b/Train.h @@ -493,5 +493,19 @@ public: // reszta może by?publiczna inline TMoverParameters *Controlled() { return mvControlled; }; void DynamicSet(TDynamicObject *d); void Silence(); + + float get_tacho(); + float get_tank_pressure(); + float get_pipe_pressure(); + float get_brake_pressure(); + float get_hv_voltage(); + std::array get_current(); + bool get_alarm(); + int get_drive_direction(); + + void set_mainctrl(int); + void set_scndctrl(int); + void set_trainbrake(float); + void set_localbrake(float); }; //--------------------------------------------------------------------------- diff --git a/World.cpp b/World.cpp index e5940308..015db2ae 100644 --- a/World.cpp +++ b/World.cpp @@ -1052,6 +1052,7 @@ bool TWorld::Update() Ground.CheckQuery(); + Ground.Update_Hidden(); Ground.Update_Lights(); { @@ -1059,10 +1060,7 @@ bool TWorld::Update() Camera.SetMatrix(cam_matrix); glm::vec3 pos(Camera.Pos.x, Camera.Pos.y, Camera.Pos.z); - glm::vec3 at = glm::vec3(0.0, 0.0, -1.0) * glm::mat3(cam_matrix); - glm::vec3 up = glm::vec3(0.0, 1.0, 0.0) * glm::mat3(cam_matrix); - - sound_man->set_listener(pos, at, up); + sound_man->set_listener(pos, glm::mat3(cam_matrix)); sound_man->update(dt); } diff --git a/World.h b/World.h index 553c9905..b471dfdc 100644 --- a/World.h +++ b/World.h @@ -108,8 +108,7 @@ TWorld(); void OnCommandGet(DaneRozkaz *pRozkaz); bool Update(); void TrainDelete(TDynamicObject *d = NULL); - TTrain const * - train() const { return Train; } + TTrain* train() { return Train; } // switches between static and dynamic daylight calculation void ToggleDaylight(); diff --git a/builds/cmake_win32.bat b/builds/cmake_win32.bat index 05d0c7b62..fdefe6c4 100644 --- a/builds/cmake_win32.bat +++ b/builds/cmake_win32.bat @@ -17,5 +17,7 @@ cmake ../.. -T v140_xp ^ -DLIBSNDFILE_INCLUDE_DIR=%DEPS_DIR%/libsndfile/include ^ -DLIBSNDFILE_LIBRARY=%DEPS_DIR%/libsndfile/lib/win32/libsndfile-1.lib ^ -DLUAJIT_INCLUDE_DIR=%DEPS_DIR%/luajit/include ^ --DLUAJIT_LIBRARIES=%DEPS_DIR%/luajit/lib/win32/lua51.lib +-DLUAJIT_LIBRARIES=%DEPS_DIR%/luajit/lib/win32/lua51.lib ^ +-Dlibserialport_INCLUDE_DIR=%DEPS_DIR%/libserialport/include ^ +-Dlibserialport_LIBRARY=%DEPS_DIR%/libserialport/lib/win32/libserialport-0.lib popd \ No newline at end of file diff --git a/builds/cmake_win64.bat b/builds/cmake_win64.bat new file mode 100644 index 00000000..96d2dc6b --- /dev/null +++ b/builds/cmake_win64.bat @@ -0,0 +1,23 @@ +if not exist deps_win call %~dp0download_windeps.bat +set DEPS_DIR="%cd%/deps_win" +if not exist build_win64 mkdir build_win64 +pushd build_win64 +cmake ../.. -A x64 ^ +-DGLEW_INCLUDE_DIR=%DEPS_DIR%/glew-2.0.0/include ^ +-DGLEW_LIBRARY=%DEPS_DIR%/glew-2.0.0/lib/Release/x64/glew32.lib ^ +-DGLFW3_ROOT_PATH=%DEPS_DIR%/glfw-3.2.1.bin.WIN64 ^ +-DGLUT_INCLUDE_DIR=%DEPS_DIR%/freeglut/include ^ +-DGLUT_glut_LIBRARY=%DEPS_DIR%/freeglut/lib/x64/freeglut.lib ^ +-DPNG_PNG_INCLUDE_DIR=%DEPS_DIR%/libpng/include ^ +-DPNG_LIBRARY=%DEPS_DIR%/libpng/lib/win64/libpng16.lib ^ +-DZLIB_INCLUDE_DIR=%DEPS_DIR%/zlib-1.2.11 ^ +-DGLM_ROOT_DIR=%DEPS_DIR%/glm-0.9.8.4 ^ +-DOPENAL_INCLUDE_DIR=%DEPS_DIR%/openal/include ^ +-DOPENAL_LIBRARY=%DEPS_DIR%/openal/lib/win64/OpenAL32.lib ^ +-DLIBSNDFILE_INCLUDE_DIR=%DEPS_DIR%/libsndfile/include ^ +-DLIBSNDFILE_LIBRARY=%DEPS_DIR%/libsndfile/lib/win64/libsndfile-1.lib ^ +-DLUAJIT_INCLUDE_DIR=%DEPS_DIR%/luajit/include ^ +-DLUAJIT_LIBRARIES=%DEPS_DIR%/luajit/lib/win64/lua51.lib ^ +-Dlibserialport_INCLUDE_DIR=%DEPS_DIR%/libserialport/include ^ +-Dlibserialport_LIBRARY=%DEPS_DIR%/libserialport/lib/win64/libserialport-0.lib +popd \ No newline at end of file diff --git a/builds/download_windeps.bat b/builds/download_windeps.bat new file mode 100644 index 00000000..24fd4428 --- /dev/null +++ b/builds/download_windeps.bat @@ -0,0 +1,2 @@ +powershell "$wc = New-Object System.Net.WebClient; $wc.DownloadFile(\"https://milek7.pl/.stuff/eu07exe/builddep4.zip\", \"%cd%\deps_win.zip\")" +powershell "$s = New-Object -ComObject shell.application; $z = $s.Namespace(\"%cd%\deps_win.zip\"); foreach ($i in $z.items()) { $s.Namespace(\"%cd%\").CopyHere($i) }" diff --git a/command.cpp b/command.cpp index 06e74454..91455a31 100644 --- a/command.cpp +++ b/command.cpp @@ -174,7 +174,9 @@ command_queue::pop( command_data &Command, std::size_t const Recipient ) { } void -command_relay::post( user_command const Command, std::uint64_t const Param1, std::uint64_t const Param2, int const Action, std::uint16_t const Recipient ) const { +command_relay::post( user_command const Command, std::uint64_t const Param1, std::uint64_t const Param2, + int const Action, std::uint16_t const Recipient, + command_data::desired_state_t state) const { auto const &command = simulation::Commands_descriptions[ static_cast( Command ) ]; if( ( command.target == command_target::vehicle ) @@ -191,7 +193,7 @@ command_relay::post( user_command const Command, std::uint64_t const Param1, std Action, Param1, Param2, - Timer::GetDeltaTime() }, + state, Timer::GetDeltaTime() }, static_cast( command.target ) | Recipient ); /* #ifdef _DEBUG diff --git a/command.h b/command.h index a06509e5..9b4e23cf 100644 --- a/command.h +++ b/command.h @@ -169,6 +169,13 @@ struct command_data { int action; // press, repeat or release std::uint64_t param1; std::uint64_t param2; + enum desired_state_t + { + TOGGLE, + OFF, + ON + }; + desired_state_t desired_state; double time_delta; }; @@ -218,7 +225,9 @@ public: // posts specified command for the specified recipient // TODO: replace uint16_t with recipient handle, based on item id void - post( user_command const Command, std::uint64_t const Param1, std::uint64_t const Param2, int const Action, std::uint16_t const Recipient ) const; + post( user_command const Command, std::uint64_t const Param1, std::uint64_t const Param2, + int const Action, std::uint16_t const Recipient, + command_data::desired_state_t state = command_data::TOGGLE ) const; private: // types // members diff --git a/dumb3d.h b/dumb3d.h index 176f8c87..1bb4a77d 100644 --- a/dumb3d.h +++ b/dumb3d.h @@ -118,6 +118,10 @@ class vector3 return true; }; + operator glm::vec3() const + { + return glm::vec3(x, y, z); + } private: }; @@ -208,6 +212,11 @@ class matrix4x4 return true; } + operator glm::mat4() const + { + return glm::make_mat4(e); + } + private: scalar_t e[16]; }; diff --git a/ref/libserialport/include/libserialport.h b/ref/libserialport/include/libserialport.h new file mode 100644 index 00000000..52a06e54 --- /dev/null +++ b/ref/libserialport/include/libserialport.h @@ -0,0 +1,1714 @@ +/* libserialport.h. Generated from libserialport.h.in by configure. */ +/* + * This file is part of the libserialport project. + * + * Copyright (C) 2013, 2015 Martin Ling + * Copyright (C) 2014 Uwe Hermann + * Copyright (C) 2014 Aurelien Jacobs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +/** + * @mainpage libserialport API + * + * Introduction + * ============ + * + * libserialport is a minimal library written in C that is intended to take + * care of the OS-specific details when writing software that uses serial ports. + * + * By writing your serial code to use libserialport, you enable it to work + * transparently on any platform supported by the library. + * + * libserialport is an open source project released under the LGPL3+ license. + * + * The library is maintained by the [sigrok](http://sigrok.org/) project. See + * the [libserialport homepage](http://sigrok.org/wiki/Libserialport) for the + * latest information. + * + * Source code is maintained in git at + * [git://sigrok.org/libserialport](http://sigrok.org/gitweb/?p=libserialport.git). + * + * Bugs are tracked at http://sigrok.org/bugzilla/. + * + * The library was conceived and designed by Martin Ling, is maintained by + * Uwe Hermann, and has received contributions from several other developers. + * See the git history for full credits. + * + * API information + * =============== + * + * The API has been designed from scratch. It does not exactly resemble the + * serial API of any particular operating system. Instead it aims to provide + * a set of functions that can reliably be implemented across all operating + * systems. These form a sufficient basis for higher level behaviour to + * be implemented in a platform independent manner. + * + * If you are porting code written for a particular OS, you may find you need + * to restructure things somewhat, or do without some specialised features. + * For particular notes on porting existing code, see @ref Porting. + * + * The following subsections will help explain the principles of the API. + * + * Headers + * ------- + * + * To use libserialport functions in your code, you should include the + * libserialport.h header, i.e. "#include ". + * + * Namespace + * --------- + * + * All identifiers defined by the public libserialport headers use the prefix + * sp_ (for functions and data types) or SP_ (for macros and constants). + * + * Functions + * --------- + * + * The functions provided by the library are documented in detail in + * the following sections: + * + * - @ref Enumeration (obtaining a list of serial ports on the system) + * - @ref Ports (opening, closing and getting information about ports) + * - @ref Configuration (baud rate, parity, etc.) + * - @ref Signals (modem control lines, breaks, etc.) + * - @ref Data (reading and writing data, and buffer management) + * - @ref Waiting (waiting for ports to be ready, integrating with event loops) + * - @ref Errors (getting error and debugging information) + * + * Data structures + * --------------- + * + * The library defines three data structures: + * + * - @ref sp_port, which represents a serial port. + * See @ref Enumeration. + * - @ref sp_port_config, which represents a port configuration. + * See @ref Configuration. + * - @ref sp_event_set, which represents a set of events. + * See @ref Waiting. + * + * All these structures are allocated and freed by library functions. It is + * the caller's responsibility to ensure that the correct calls are made to + * free allocated structures after use. + * + * Return codes and error handling + * ------------------------------- + * + * Most functions have return type @ref sp_return and can return only four + * possible error values: + * + * - @ref SP_ERR_ARG means that a function was called with invalid + * arguments. This implies a bug in the caller. The arguments passed would + * be invalid regardless of the underlying OS or serial device involved. + * + * - @ref SP_ERR_FAIL means that the OS reported a failure. The error code or + * message provided by the OS can be obtained by calling sp_last_error_code() + * or sp_last_error_message(). + * + * - @ref SP_ERR_SUPP indicates that there is no support for the requested + * operation in the current OS, driver or device. No error message is + * available from the OS in this case. There is either no way to request + * the operation in the first place, or libserialport does not know how to + * do so in the current version. + * + * - @ref SP_ERR_MEM indicates that a memory allocation failed. + * + * All of these error values are negative. + * + * Calls that succeed return @ref SP_OK, which is equal to zero. Some functions + * declared @ref sp_return can also return a positive value for a successful + * numeric result, e.g. sp_blocking_read() or sp_blocking_write(). + * + * An error message is only available via sp_last_error_message() in the case + * where SP_ERR_FAIL was returned by the previous function call. The error + * message returned is that provided by the OS, using the current language + * settings. It is an error to call sp_last_error_code() or + * sp_last_error_message() except after a previous function call returned + * SP_ERR_FAIL. The library does not define its own error codes or messages + * to accompany other return codes. + * + * Thread safety + * ------------- + * + * Certain combinations of calls can be made concurrently, as follows. + * + * - Calls using different ports may always be made concurrently, i.e. + * it is safe for separate threads to handle their own ports. + * + * - Calls using the same port may be made concurrently when one call + * is a read operation and one call is a write operation, i.e. it is safe + * to use separate "reader" and "writer" threads for the same port. See + * below for which operations meet these definitions. + * + * Read operations: + * + * - sp_blocking_read() + * - sp_blocking_read_next() + * - sp_nonblocking_read() + * - sp_input_waiting() + * - sp_flush() with @ref SP_BUF_INPUT only. + * - sp_wait() with @ref SP_EVENT_RX_READY only. + * + * Write operations: + * + * - sp_blocking_write() + * - sp_nonblocking_write() + * - sp_output_waiting() + * - sp_drain() + * - sp_flush() with @ref SP_BUF_OUTPUT only. + * - sp_wait() with @ref SP_EVENT_TX_READY only. + * + * If two calls, on the same port, do not fit into one of these categories + * each, then they may not be made concurrently. + * + * Debugging + * --------- + * + * The library can output extensive tracing and debugging information. The + * simplest way to use this is to set the environment variable + * LIBSERIALPORT_DEBUG to any value; messages will then be output to the + * standard error stream. + * + * This behaviour is implemented by a default debug message handling + * callback. An alternative callback can be set using sp_set_debug_handler(), + * in order to e.g. redirect the output elsewhere or filter it. + * + * No guarantees are made about the content of the debug output; it is chosen + * to suit the needs of the developers and may change between releases. + * + * @anchor Porting + * Porting + * ------- + * + * The following guidelines may help when porting existing OS-specific code + * to use libserialport. + * + * ### Porting from Unix-like systems ### + * + * There are two main differences to note when porting code written for Unix. + * + * The first is that Unix traditionally provides a wide range of functionality + * for dealing with serial devices at the OS level; this is exposed through the + * termios API and dates to the days when serial terminals were common. If your + * code relies on many of these facilities you will need to adapt it, because + * libserialport provides only a raw binary channel with no special handling. + * + * The second relates to blocking versus non-blocking I/O behaviour. In + * Unix-like systems this is normally specified by setting the O_NONBLOCK + * flag on the file descriptor, affecting the semantics of subsequent read() + * and write() calls. + * + * In libserialport, blocking and nonblocking operations are both available at + * any time. If your existing code ѕets O_NONBLOCK, you should use + * sp_nonblocking_read() and sp_nonblocking_write() to get the same behaviour + * as your existing read() and write() calls. If it does not, you should use + * sp_blocking_read() and sp_blocking_write() instead. You may also find + * sp_blocking_read_next() useful, which reproduces the semantics of a blocking + * read() with VTIME = 0 and VMIN = 1 set in termios. + * + * Finally, you should take care if your program uses custom signal handlers. + * The blocking calls provided by libserialport will restart system calls that + * return with EINTR, so you will need to make your own arrangements if you + * need to interrupt blocking operations when your signal handlers are called. + * This is not an issue if you only use the default handlers. + * + * ### Porting from Windows ### + * + * The main consideration when porting from Windows is that there is no + * direct equivalent for overlapped I/O operations. + * + * If your program does not use overlapped I/O, you can simply use + * sp_blocking_read() and sp_blocking_write() as direct equivalents for + * ReadFile() and WriteFile(). You may also find sp_blocking_read_next() + * useful, which reproduces the special semantics of ReadFile() with + * ReadIntervalTimeout and ReadTotalTimeoutMultiplier set to MAXDWORD + * and 0 < ReadTotalTimeoutConstant < MAXDWORD. + * + * If your program makes use of overlapped I/O to continue work while a serial + * operation is in progress, then you can achieve the same results using + * sp_nonblocking_read() and sp_nonblocking_write(). + * + * Generally, overlapped I/O is combined with either waiting for completion + * once there is no more background work to do (using WaitForSingleObject() or + * WaitForMultipleObjects()), or periodically checking for completion with + * GetOverlappedResult(). If the aim is to start a new operation for further + * data once the previous one has completed, you can instead simply call the + * nonblocking functions again with the next data. If you need to wait for + * completion, use sp_wait() to determine when the port is ready to send or + * receive further data. + */ + +#ifndef LIBSERIALPORT_LIBSERIALPORT_H +#define LIBSERIALPORT_LIBSERIALPORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** Return values. */ +enum sp_return { + /** Operation completed successfully. */ + SP_OK = 0, + /** Invalid arguments were passed to the function. */ + SP_ERR_ARG = -1, + /** A system error occurred while executing the operation. */ + SP_ERR_FAIL = -2, + /** A memory allocation failed while executing the operation. */ + SP_ERR_MEM = -3, + /** The requested operation is not supported by this system or device. */ + SP_ERR_SUPP = -4 +}; + +/** Port access modes. */ +enum sp_mode { + /** Open port for read access. */ + SP_MODE_READ = 1, + /** Open port for write access. */ + SP_MODE_WRITE = 2, + /** Open port for read and write access. @since 0.1.1 */ + SP_MODE_READ_WRITE = 3 +}; + +/** Port events. */ +enum sp_event { + /** Data received and ready to read. */ + SP_EVENT_RX_READY = 1, + /** Ready to transmit new data. */ + SP_EVENT_TX_READY = 2, + /** Error occurred. */ + SP_EVENT_ERROR = 4 +}; + +/** Buffer selection. */ +enum sp_buffer { + /** Input buffer. */ + SP_BUF_INPUT = 1, + /** Output buffer. */ + SP_BUF_OUTPUT = 2, + /** Both buffers. */ + SP_BUF_BOTH = 3 +}; + +/** Parity settings. */ +enum sp_parity { + /** Special value to indicate setting should be left alone. */ + SP_PARITY_INVALID = -1, + /** No parity. */ + SP_PARITY_NONE = 0, + /** Odd parity. */ + SP_PARITY_ODD = 1, + /** Even parity. */ + SP_PARITY_EVEN = 2, + /** Mark parity. */ + SP_PARITY_MARK = 3, + /** Space parity. */ + SP_PARITY_SPACE = 4 +}; + +/** RTS pin behaviour. */ +enum sp_rts { + /** Special value to indicate setting should be left alone. */ + SP_RTS_INVALID = -1, + /** RTS off. */ + SP_RTS_OFF = 0, + /** RTS on. */ + SP_RTS_ON = 1, + /** RTS used for flow control. */ + SP_RTS_FLOW_CONTROL = 2 +}; + +/** CTS pin behaviour. */ +enum sp_cts { + /** Special value to indicate setting should be left alone. */ + SP_CTS_INVALID = -1, + /** CTS ignored. */ + SP_CTS_IGNORE = 0, + /** CTS used for flow control. */ + SP_CTS_FLOW_CONTROL = 1 +}; + +/** DTR pin behaviour. */ +enum sp_dtr { + /** Special value to indicate setting should be left alone. */ + SP_DTR_INVALID = -1, + /** DTR off. */ + SP_DTR_OFF = 0, + /** DTR on. */ + SP_DTR_ON = 1, + /** DTR used for flow control. */ + SP_DTR_FLOW_CONTROL = 2 +}; + +/** DSR pin behaviour. */ +enum sp_dsr { + /** Special value to indicate setting should be left alone. */ + SP_DSR_INVALID = -1, + /** DSR ignored. */ + SP_DSR_IGNORE = 0, + /** DSR used for flow control. */ + SP_DSR_FLOW_CONTROL = 1 +}; + +/** XON/XOFF flow control behaviour. */ +enum sp_xonxoff { + /** Special value to indicate setting should be left alone. */ + SP_XONXOFF_INVALID = -1, + /** XON/XOFF disabled. */ + SP_XONXOFF_DISABLED = 0, + /** XON/XOFF enabled for input only. */ + SP_XONXOFF_IN = 1, + /** XON/XOFF enabled for output only. */ + SP_XONXOFF_OUT = 2, + /** XON/XOFF enabled for input and output. */ + SP_XONXOFF_INOUT = 3 +}; + +/** Standard flow control combinations. */ +enum sp_flowcontrol { + /** No flow control. */ + SP_FLOWCONTROL_NONE = 0, + /** Software flow control using XON/XOFF characters. */ + SP_FLOWCONTROL_XONXOFF = 1, + /** Hardware flow control using RTS/CTS signals. */ + SP_FLOWCONTROL_RTSCTS = 2, + /** Hardware flow control using DTR/DSR signals. */ + SP_FLOWCONTROL_DTRDSR = 3 +}; + +/** Input signals. */ +enum sp_signal { + /** Clear to send. */ + SP_SIG_CTS = 1, + /** Data set ready. */ + SP_SIG_DSR = 2, + /** Data carrier detect. */ + SP_SIG_DCD = 4, + /** Ring indicator. */ + SP_SIG_RI = 8 +}; + +/** + * Transport types. + * + * @since 0.1.1 + */ +enum sp_transport { + /** Native platform serial port. @since 0.1.1 */ + SP_TRANSPORT_NATIVE, + /** USB serial port adapter. @since 0.1.1 */ + SP_TRANSPORT_USB, + /** Bluetooth serial port adapter. @since 0.1.1 */ + SP_TRANSPORT_BLUETOOTH +}; + +/** + * @struct sp_port + * An opaque structure representing a serial port. + */ +struct sp_port; + +/** + * @struct sp_port_config + * An opaque structure representing the configuration for a serial port. + */ +struct sp_port_config; + +/** + * @struct sp_event_set + * A set of handles to wait on for events. + */ +struct sp_event_set { + /** Array of OS-specific handles. */ + void *handles; + /** Array of bitmasks indicating which events apply for each handle. */ + enum sp_event *masks; + /** Number of handles. */ + unsigned int count; +}; + +/** + * @defgroup Enumeration Port enumeration + * + * Enumerating the serial ports of a system. + * + * @{ + */ + +/** + * Obtain a pointer to a new sp_port structure representing the named port. + * + * The user should allocate a variable of type "struct sp_port *" and pass a + * pointer to this to receive the result. + * + * The result should be freed after use by calling sp_free_port(). + * + * @param[in] portname The OS-specific name of a serial port. Must not be NULL. + * @param[out] port_ptr If any error is returned, the variable pointed to by + * port_ptr will be set to NULL. Otherwise, it will be set + * to point to the newly allocated port. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr); + +/** + * Free a port structure obtained from sp_get_port_by_name() or sp_copy_port(). + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @since 0.1.0 + */ +void sp_free_port(struct sp_port *port); + +/** + * List the serial ports available on the system. + * + * The result obtained is an array of pointers to sp_port structures, + * terminated by a NULL. The user should allocate a variable of type + * "struct sp_port **" and pass a pointer to this to receive the result. + * + * The result should be freed after use by calling sp_free_port_list(). + * If a port from the list is to be used after freeing the list, it must be + * copied first using sp_copy_port(). + * + * @param[out] list_ptr If any error is returned, the variable pointed to by + * list_ptr will be set to NULL. Otherwise, it will be set + * to point to the newly allocated array. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_list_ports(struct sp_port ***list_ptr); + +/** + * Make a new copy of an sp_port structure. + * + * The user should allocate a variable of type "struct sp_port *" and pass a + * pointer to this to receive the result. + * + * The copy should be freed after use by calling sp_free_port(). + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] copy_ptr If any error is returned, the variable pointed to by + * copy_ptr will be set to NULL. Otherwise, it will be set + * to point to the newly allocated copy. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_copy_port(const struct sp_port *port, struct sp_port **copy_ptr); + +/** + * Free a port list obtained from sp_list_ports(). + * + * This will also free all the sp_port structures referred to from the list; + * any that are to be retained must be copied first using sp_copy_port(). + * + * @param[in] ports Pointer to a list of port structures. Must not be NULL. + * + * @since 0.1.0 + */ +void sp_free_port_list(struct sp_port **ports); + +/** + * @} + * @defgroup Ports Port handling + * + * Opening, closing and querying ports. + * + * @{ + */ + +/** + * Open the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] flags Flags to use when opening the serial port. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_open(struct sp_port *port, enum sp_mode flags); + +/** + * Close the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_close(struct sp_port *port); + +/** + * Get the name of a port. + * + * The name returned is whatever is normally used to refer to a port on the + * current operating system; e.g. for Windows it will usually be a "COMn" + * device name, and for Unix it will be a device path beginning with "/dev/". + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port name, or NULL if an invalid port is passed. The name + * string is part of the port structure and may not be used after + * the port structure has been freed. + * + * @since 0.1.0 + */ +char *sp_get_port_name(const struct sp_port *port); + +/** + * Get a description for a port, to present to end user. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port description, or NULL if an invalid port is passed. + * The description string is part of the port structure and may not + * be used after the port structure has been freed. + * + * @since 0.1.1 + */ +char *sp_get_port_description(const struct sp_port *port); + +/** + * Get the transport type used by a port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port transport type. + * + * @since 0.1.1 + */ +enum sp_transport sp_get_port_transport(const struct sp_port *port); + +/** + * Get the USB bus number and address on bus of a USB serial adapter port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] usb_bus Pointer to a variable to store the USB bus. + * Can be NULL (in that case it will be ignored). + * @param[out] usb_address Pointer to a variable to store the USB address. + * Can be NULL (in that case it will be ignored). + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.1 + */ +enum sp_return sp_get_port_usb_bus_address(const struct sp_port *port, + int *usb_bus, int *usb_address); + +/** + * Get the USB Vendor ID and Product ID of a USB serial adapter port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] usb_vid Pointer to a variable to store the USB VID. + * Can be NULL (in that case it will be ignored). + * @param[out] usb_pid Pointer to a variable to store the USB PID. + * Can be NULL (in that case it will be ignored). + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.1 + */ +enum sp_return sp_get_port_usb_vid_pid(const struct sp_port *port, int *usb_vid, int *usb_pid); + +/** + * Get the USB manufacturer string of a USB serial adapter port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port manufacturer string, or NULL if an invalid port is passed. + * The manufacturer string is part of the port structure and may not + * be used after the port structure has been freed. + * + * @since 0.1.1 + */ +char *sp_get_port_usb_manufacturer(const struct sp_port *port); + +/** + * Get the USB product string of a USB serial adapter port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port product string, or NULL if an invalid port is passed. + * The product string is part of the port structure and may not be + * used after the port structure has been freed. + * + * @since 0.1.1 + */ +char *sp_get_port_usb_product(const struct sp_port *port); + +/** + * Get the USB serial number string of a USB serial adapter port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port serial number, or NULL if an invalid port is passed. + * The serial number string is part of the port structure and may + * not be used after the port structure has been freed. + * + * @since 0.1.1 + */ +char *sp_get_port_usb_serial(const struct sp_port *port); + +/** + * Get the MAC address of a Bluetooth serial adapter port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return The port MAC address, or NULL if an invalid port is passed. + * The MAC address string is part of the port structure and may not + * be used after the port structure has been freed. + * + * @since 0.1.1 + */ +char *sp_get_port_bluetooth_address(const struct sp_port *port); + +/** + * Get the operating system handle for a port. + * + * The type of the handle depends on the operating system. On Unix based + * systems, the handle is a file descriptor of type "int". On Windows, the + * handle is of type "HANDLE". The user should allocate a variable of the + * appropriate type and pass a pointer to this to receive the result. + * + * To obtain a valid handle, the port must first be opened by calling + * sp_open() using the same port structure. + * + * After the port is closed or the port structure freed, the handle may + * no longer be valid. + * + * @warning This feature is provided so that programs may make use of + * OS-specific functionality where desired. Doing so obviously + * comes at a cost in portability. It also cannot be guaranteed + * that direct usage of the OS handle will not conflict with the + * library's own usage of the port. Be careful. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] result_ptr If any error is returned, the variable pointed to by + * result_ptr will have unknown contents and should not + * be used. Otherwise, it will be set to point to the + * OS handle. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_port_handle(const struct sp_port *port, void *result_ptr); + +/** + * @} + * + * @defgroup Configuration Configuration + * + * Setting and querying serial port parameters. + * @{ + */ + +/** + * Allocate a port configuration structure. + * + * The user should allocate a variable of type "struct sp_port_config *" and + * pass a pointer to this to receive the result. The variable will be updated + * to point to the new configuration structure. The structure is opaque and + * must be accessed via the functions provided. + * + * All parameters in the structure will be initialised to special values which + * are ignored by sp_set_config(). + * + * The structure should be freed after use by calling sp_free_config(). + * + * @param[out] config_ptr If any error is returned, the variable pointed to by + * config_ptr will be set to NULL. Otherwise, it will + * be set to point to the allocated config structure. + * Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_new_config(struct sp_port_config **config_ptr); + +/** + * Free a port configuration structure. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * + * @since 0.1.0 + */ +void sp_free_config(struct sp_port_config *config); + +/** + * Get the current configuration of the specified serial port. + * + * The user should allocate a configuration structure using sp_new_config() + * and pass this as the config parameter. The configuration structure will + * be updated with the port configuration. + * + * Any parameters that are configured with settings not recognised or + * supported by libserialport, will be set to special values that are + * ignored by sp_set_config(). + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] config Pointer to a configuration structure that will hold + * the result. Upon errors the contents of the config + * struct will not be changed. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config(struct sp_port *port, struct sp_port_config *config); + +/** + * Set the configuration for the specified serial port. + * + * For each parameter in the configuration, there is a special value (usually + * -1, but see the documentation for each field). These values will be ignored + * and the corresponding setting left unchanged on the port. + * + * Upon errors, the configuration of the serial port is unknown since + * partial/incomplete config updates may have happened. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config *config); + +/** + * Set the baud rate for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] baudrate Baud rate in bits per second. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_baudrate(struct sp_port *port, int baudrate); + +/** + * Get the baud rate from a port configuration. + * + * The user should allocate a variable of type int and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] baudrate_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_baudrate(const struct sp_port_config *config, int *baudrate_ptr); + +/** + * Set the baud rate in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] baudrate Baud rate in bits per second, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_baudrate(struct sp_port_config *config, int baudrate); + +/** + * Set the data bits for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] bits Number of data bits. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_bits(struct sp_port *port, int bits); + +/** + * Get the data bits from a port configuration. + * + * The user should allocate a variable of type int and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] bits_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_bits(const struct sp_port_config *config, int *bits_ptr); + +/** + * Set the data bits in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] bits Number of data bits, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_bits(struct sp_port_config *config, int bits); + +/** + * Set the parity setting for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] parity Parity setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_parity(struct sp_port *port, enum sp_parity parity); + +/** + * Get the parity setting from a port configuration. + * + * The user should allocate a variable of type enum sp_parity and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] parity_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_parity(const struct sp_port_config *config, enum sp_parity *parity_ptr); + +/** + * Set the parity setting in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] parity Parity setting, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_parity(struct sp_port_config *config, enum sp_parity parity); + +/** + * Set the stop bits for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] stopbits Number of stop bits. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_stopbits(struct sp_port *port, int stopbits); + +/** + * Get the stop bits from a port configuration. + * + * The user should allocate a variable of type int and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] stopbits_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_stopbits(const struct sp_port_config *config, int *stopbits_ptr); + +/** + * Set the stop bits in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] stopbits Number of stop bits, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_stopbits(struct sp_port_config *config, int stopbits); + +/** + * Set the RTS pin behaviour for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] rts RTS pin mode. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_rts(struct sp_port *port, enum sp_rts rts); + +/** + * Get the RTS pin behaviour from a port configuration. + * + * The user should allocate a variable of type enum sp_rts and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] rts_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_rts(const struct sp_port_config *config, enum sp_rts *rts_ptr); + +/** + * Set the RTS pin behaviour in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] rts RTS pin mode, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_rts(struct sp_port_config *config, enum sp_rts rts); + +/** + * Set the CTS pin behaviour for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] cts CTS pin mode. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_cts(struct sp_port *port, enum sp_cts cts); + +/** + * Get the CTS pin behaviour from a port configuration. + * + * The user should allocate a variable of type enum sp_cts and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] cts_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_cts(const struct sp_port_config *config, enum sp_cts *cts_ptr); + +/** + * Set the CTS pin behaviour in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] cts CTS pin mode, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_cts(struct sp_port_config *config, enum sp_cts cts); + +/** + * Set the DTR pin behaviour for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] dtr DTR pin mode. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_dtr(struct sp_port *port, enum sp_dtr dtr); + +/** + * Get the DTR pin behaviour from a port configuration. + * + * The user should allocate a variable of type enum sp_dtr and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] dtr_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_dtr(const struct sp_port_config *config, enum sp_dtr *dtr_ptr); + +/** + * Set the DTR pin behaviour in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] dtr DTR pin mode, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_dtr(struct sp_port_config *config, enum sp_dtr dtr); + +/** + * Set the DSR pin behaviour for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] dsr DSR pin mode. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_dsr(struct sp_port *port, enum sp_dsr dsr); + +/** + * Get the DSR pin behaviour from a port configuration. + * + * The user should allocate a variable of type enum sp_dsr and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] dsr_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_dsr(const struct sp_port_config *config, enum sp_dsr *dsr_ptr); + +/** + * Set the DSR pin behaviour in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] dsr DSR pin mode, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_dsr(struct sp_port_config *config, enum sp_dsr dsr); + +/** + * Set the XON/XOFF configuration for the specified serial port. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] xon_xoff XON/XOFF mode. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_xon_xoff(struct sp_port *port, enum sp_xonxoff xon_xoff); + +/** + * Get the XON/XOFF configuration from a port configuration. + * + * The user should allocate a variable of type enum sp_xonxoff and + * pass a pointer to this to receive the result. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[out] xon_xoff_ptr Pointer to a variable to store the result. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_config_xon_xoff(const struct sp_port_config *config, enum sp_xonxoff *xon_xoff_ptr); + +/** + * Set the XON/XOFF configuration in a port configuration. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] xon_xoff XON/XOFF mode, or -1 to retain the current setting. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_xon_xoff(struct sp_port_config *config, enum sp_xonxoff xon_xoff); + +/** + * Set the flow control type in a port configuration. + * + * This function is a wrapper that sets the RTS, CTS, DTR, DSR and + * XON/XOFF settings as necessary for the specified flow control + * type. For more fine-grained control of these settings, use their + * individual configuration functions. + * + * @param[in] config Pointer to a configuration structure. Must not be NULL. + * @param[in] flowcontrol Flow control setting to use. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_config_flowcontrol(struct sp_port_config *config, enum sp_flowcontrol flowcontrol); + +/** + * Set the flow control type for the specified serial port. + * + * This function is a wrapper that sets the RTS, CTS, DTR, DSR and + * XON/XOFF settings as necessary for the specified flow control + * type. For more fine-grained control of these settings, use their + * individual configuration functions. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] flowcontrol Flow control setting to use. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flowcontrol); + +/** + * @} + * + * @defgroup Data Data handling + * + * Reading, writing, and flushing data. + * + * @{ + */ + +/** + * Read bytes from the specified serial port, blocking until complete. + * + * @warning If your program runs on Unix, defines its own signal handlers, and + * needs to abort blocking reads when these are called, then you + * should not use this function. It repeats system calls that return + * with EINTR. To be able to abort a read from a signal handler, you + * should implement your own blocking read using sp_nonblocking_read() + * together with a blocking method that makes sense for your program. + * E.g. you can obtain the file descriptor for an open port using + * sp_get_port_handle() and use this to call select() or pselect(), + * with appropriate arrangements to return if a signal is received. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] buf Buffer in which to store the bytes read. Must not be NULL. + * @param[in] count Requested number of bytes to read. + * @param[in] timeout_ms Timeout in milliseconds, or zero to wait indefinitely. + * + * @return The number of bytes read on success, or a negative error code. If + * the number of bytes returned is less than that requested, the + * timeout was reached before the requested number of bytes was + * available. If timeout is zero, the function will always return + * either the requested number of bytes or a negative error code. + * + * @since 0.1.0 + */ +enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, unsigned int timeout_ms); + +/** + * Read bytes from the specified serial port, returning as soon as any data is + * available. + * + * @warning If your program runs on Unix, defines its own signal handlers, and + * needs to abort blocking reads when these are called, then you + * should not use this function. It repeats system calls that return + * with EINTR. To be able to abort a read from a signal handler, you + * should implement your own blocking read using sp_nonblocking_read() + * together with a blocking method that makes sense for your program. + * E.g. you can obtain the file descriptor for an open port using + * sp_get_port_handle() and use this to call select() or pselect(), + * with appropriate arrangements to return if a signal is received. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] buf Buffer in which to store the bytes read. Must not be NULL. + * @param[in] count Maximum number of bytes to read. Must not be zero. + * @param[in] timeout_ms Timeout in milliseconds, or zero to wait indefinitely. + * + * @return The number of bytes read on success, or a negative error code. If + * the result is zero, the timeout was reached before any bytes were + * available. If timeout_ms is zero, the function will always return + * either at least one byte, or a negative error code. + * + * @since 0.1.1 + */ +enum sp_return sp_blocking_read_next(struct sp_port *port, void *buf, size_t count, unsigned int timeout_ms); + +/** + * Read bytes from the specified serial port, without blocking. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] buf Buffer in which to store the bytes read. Must not be NULL. + * @param[in] count Maximum number of bytes to read. + * + * @return The number of bytes read on success, or a negative error code. The + * number of bytes returned may be any number from zero to the maximum + * that was requested. + * + * @since 0.1.0 + */ +enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count); + +/** + * Write bytes to the specified serial port, blocking until complete. + * + * Note that this function only ensures that the accepted bytes have been + * written to the OS; they may be held in driver or hardware buffers and not + * yet physically transmitted. To check whether all written bytes have actually + * been transmitted, use the sp_output_waiting() function. To wait until all + * written bytes have actually been transmitted, use the sp_drain() function. + * + * @warning If your program runs on Unix, defines its own signal handlers, and + * needs to abort blocking writes when these are called, then you + * should not use this function. It repeats system calls that return + * with EINTR. To be able to abort a write from a signal handler, you + * should implement your own blocking write using sp_nonblocking_write() + * together with a blocking method that makes sense for your program. + * E.g. you can obtain the file descriptor for an open port using + * sp_get_port_handle() and use this to call select() or pselect(), + * with appropriate arrangements to return if a signal is received. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] buf Buffer containing the bytes to write. Must not be NULL. + * @param[in] count Requested number of bytes to write. + * @param[in] timeout_ms Timeout in milliseconds, or zero to wait indefinitely. + * + * @return The number of bytes written on success, or a negative error code. + * If the number of bytes returned is less than that requested, the + * timeout was reached before the requested number of bytes was + * written. If timeout is zero, the function will always return + * either the requested number of bytes or a negative error code. In + * the event of an error there is no way to determine how many bytes + * were sent before the error occurred. + * + * @since 0.1.0 + */ +enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t count, unsigned int timeout_ms); + +/** + * Write bytes to the specified serial port, without blocking. + * + * Note that this function only ensures that the accepted bytes have been + * written to the OS; they may be held in driver or hardware buffers and not + * yet physically transmitted. To check whether all written bytes have actually + * been transmitted, use the sp_output_waiting() function. To wait until all + * written bytes have actually been transmitted, use the sp_drain() function. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] buf Buffer containing the bytes to write. Must not be NULL. + * @param[in] count Maximum number of bytes to write. + * + * @return The number of bytes written on success, or a negative error code. + * The number of bytes returned may be any number from zero to the + * maximum that was requested. + * + * @since 0.1.0 + */ +enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_t count); + +/** + * Gets the number of bytes waiting in the input buffer. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return Number of bytes waiting on success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_input_waiting(struct sp_port *port); + +/** + * Gets the number of bytes waiting in the output buffer. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return Number of bytes waiting on success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_output_waiting(struct sp_port *port); + +/** + * Flush serial port buffers. Data in the selected buffer(s) is discarded. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] buffers Which buffer(s) to flush. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers); + +/** + * Wait for buffered data to be transmitted. + * + * @warning If your program runs on Unix, defines its own signal handlers, and + * needs to abort draining the output buffer when when these are + * called, then you should not use this function. It repeats system + * calls that return with EINTR. To be able to abort a drain from a + * signal handler, you would need to implement your own blocking + * drain by polling the result of sp_output_waiting(). + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_drain(struct sp_port *port); + +/** + * @} + * + * @defgroup Waiting Waiting + * + * Waiting for events and timeout handling. + * + * @{ + */ + +/** + * Allocate storage for a set of events. + * + * The user should allocate a variable of type struct sp_event_set *, + * then pass a pointer to this variable to receive the result. + * + * The result should be freed after use by calling sp_free_event_set(). + * + * @param[out] result_ptr If any error is returned, the variable pointed to by + * result_ptr will be set to NULL. Otherwise, it will + * be set to point to the event set. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_new_event_set(struct sp_event_set **result_ptr); + +/** + * Add events to a struct sp_event_set for a given port. + * + * The port must first be opened by calling sp_open() using the same port + * structure. + * + * After the port is closed or the port structure freed, the results may + * no longer be valid. + * + * @param[in,out] event_set Event set to update. Must not be NULL. + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[in] mask Bitmask of events to be waited for. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_add_port_events(struct sp_event_set *event_set, + const struct sp_port *port, enum sp_event mask); + +/** + * Wait for any of a set of events to occur. + * + * @param[in] event_set Event set to wait on. Must not be NULL. + * @param[in] timeout_ms Timeout in milliseconds, or zero to wait indefinitely. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_wait(struct sp_event_set *event_set, unsigned int timeout_ms); + +/** + * Free a structure allocated by sp_new_event_set(). + * + * @param[in] event_set Event set to free. Must not be NULL. + * + * @since 0.1.0 + */ +void sp_free_event_set(struct sp_event_set *event_set); + +/** + * @} + * + * @defgroup Signals Signals + * + * Port signalling operations. + * + * @{ + */ + +/** + * Gets the status of the control signals for the specified port. + * + * The user should allocate a variable of type "enum sp_signal" and pass a + * pointer to this variable to receive the result. The result is a bitmask + * in which individual signals can be checked by bitwise OR with values of + * the sp_signal enum. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * @param[out] signal_mask Pointer to a variable to receive the result. + * Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_get_signals(struct sp_port *port, enum sp_signal *signal_mask); + +/** + * Put the port transmit line into the break state. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_start_break(struct sp_port *port); + +/** + * Take the port transmit line out of the break state. + * + * @param[in] port Pointer to a port structure. Must not be NULL. + * + * @return SP_OK upon success, a negative error code otherwise. + * + * @since 0.1.0 + */ +enum sp_return sp_end_break(struct sp_port *port); + +/** + * @} + * + * @defgroup Errors Errors + * + * Obtaining error information. + * + * @{ + */ + +/** + * Get the error code for a failed operation. + * + * In order to obtain the correct result, this function should be called + * straight after the failure, before executing any other system operations. + * The result is thread-specific, and only valid when called immediately + * after a previous call returning SP_ERR_FAIL. + * + * @return The system's numeric code for the error that caused the last + * operation to fail. + * + * @since 0.1.0 + */ +int sp_last_error_code(void); + +/** + * Get the error message for a failed operation. + * + * In order to obtain the correct result, this function should be called + * straight after the failure, before executing other system operations. + * The result is thread-specific, and only valid when called immediately + * after a previous call returning SP_ERR_FAIL. + * + * @return The system's message for the error that caused the last + * operation to fail. This string may be allocated by the function, + * and should be freed after use by calling sp_free_error_message(). + * + * @since 0.1.0 + */ +char *sp_last_error_message(void); + +/** + * Free an error message returned by sp_last_error_message(). + * + * @param[in] message The error message string to free. Must not be NULL. + * + * @since 0.1.0 + */ +void sp_free_error_message(char *message); + +/** + * Set the handler function for library debugging messages. + * + * Debugging messages are generated by the library during each operation, + * to help in diagnosing problems. The handler will be called for each + * message. The handler can be set to NULL to ignore all debug messages. + * + * The handler function should accept a format string and variable length + * argument list, in the same manner as e.g. printf(). + * + * The default handler is sp_default_debug_handler(). + * + * @param[in] handler The handler function to use. Can be NULL (in that case + * all debug messages will be ignored). + * + * @since 0.1.0 + */ +void sp_set_debug_handler(void (*handler)(const char *format, ...)); + +/** + * Default handler function for library debugging messages. + * + * This function prints debug messages to the standard error stream if the + * environment variable LIBSERIALPORT_DEBUG is set. Otherwise, they are + * ignored. + * + * @param[in] format The format string to use. Must not be NULL. + * @param[in] ... The variable length argument list to use. + * + * @since 0.1.0 + */ +void sp_default_debug_handler(const char *format, ...); + +/** @} */ + +/** + * @defgroup Versions Versions + * + * Version number querying functions, definitions, and macros. + * + * This set of API calls returns two different version numbers related + * to libserialport. The "package version" is the release version number of the + * libserialport tarball in the usual "major.minor.micro" format, e.g. "0.1.0". + * + * The "library version" is independent of that; it is the libtool version + * number in the "current:revision:age" format, e.g. "2:0:0". + * See http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning for details. + * + * Both version numbers (and/or individual components of them) can be + * retrieved via the API calls at runtime, and/or they can be checked at + * compile/preprocessor time using the respective macros. + * + * @{ + */ + +/* + * Package version macros (can be used for conditional compilation). + */ + +/** The libserialport package 'major' version number. */ +#define SP_PACKAGE_VERSION_MAJOR 0 + +/** The libserialport package 'minor' version number. */ +#define SP_PACKAGE_VERSION_MINOR 1 + +/** The libserialport package 'micro' version number. */ +#define SP_PACKAGE_VERSION_MICRO 1 + +/** The libserialport package version ("major.minor.micro") as string. */ +#define SP_PACKAGE_VERSION_STRING "0.1.1" + +/* + * Library/libtool version macros (can be used for conditional compilation). + */ + +/** The libserialport libtool 'current' version number. */ +#define SP_LIB_VERSION_CURRENT 1 + +/** The libserialport libtool 'revision' version number. */ +#define SP_LIB_VERSION_REVISION 0 + +/** The libserialport libtool 'age' version number. */ +#define SP_LIB_VERSION_AGE 1 + +/** The libserialport libtool version ("current:revision:age") as string. */ +#define SP_LIB_VERSION_STRING "1:0:1" + +/** + * Get the major libserialport package version number. + * + * @return The major package version number. + * + * @since 0.1.0 + */ +int sp_get_major_package_version(void); + +/** + * Get the minor libserialport package version number. + * + * @return The minor package version number. + * + * @since 0.1.0 + */ +int sp_get_minor_package_version(void); + +/** + * Get the micro libserialport package version number. + * + * @return The micro package version number. + * + * @since 0.1.0 + */ +int sp_get_micro_package_version(void); + +/** + * Get the libserialport package version number as a string. + * + * @return The package version number string. The returned string is + * static and thus should NOT be free'd by the caller. + * + * @since 0.1.0 + */ +const char *sp_get_package_version_string(void); + +/** + * Get the "current" part of the libserialport library version number. + * + * @return The "current" library version number. + * + * @since 0.1.0 + */ +int sp_get_current_lib_version(void); + +/** + * Get the "revision" part of the libserialport library version number. + * + * @return The "revision" library version number. + * + * @since 0.1.0 + */ +int sp_get_revision_lib_version(void); + +/** + * Get the "age" part of the libserialport library version number. + * + * @return The "age" library version number. + * + * @since 0.1.0 + */ +int sp_get_age_lib_version(void); + +/** + * Get the libserialport library version number as a string. + * + * @return The library version number string. The returned string is + * static and thus should NOT be free'd by the caller. + * + * @since 0.1.0 + */ +const char *sp_get_lib_version_string(void); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ref/libserialport/lib/win32/libserialport-0.lib b/ref/libserialport/lib/win32/libserialport-0.lib new file mode 100644 index 00000000..909c1fb4 Binary files /dev/null and b/ref/libserialport/lib/win32/libserialport-0.lib differ diff --git a/ref/libserialport/lib/win64/libserialport-0.lib b/ref/libserialport/lib/win64/libserialport-0.lib new file mode 100644 index 00000000..f65a406e Binary files /dev/null and b/ref/libserialport/lib/win64/libserialport-0.lib differ diff --git a/renderer.cpp b/renderer.cpp index 9f849129..f06d2a3b 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1345,19 +1345,6 @@ opengl_renderer::Render( TGround *Ground ) { m_drawqueue.clear(); - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - // rednerowanie globalnych (nie za często?) - for( TGroundNode *node = Ground->srGlobal.nRenderHidden; node; node = node->nNext3 ) { - node->RenderHidden(); - } - break; - } - default: { - break; - } - } - glm::vec3 const cameraposition { m_renderpass.camera.position() }; int const camerax = static_cast( std::floor( cameraposition.x / 1000.0f ) + iNumRects / 2 ); int const cameraz = static_cast( std::floor( cameraposition.z / 1000.0f ) + iNumRects / 2 ); @@ -1374,20 +1361,6 @@ opengl_renderer::Render( TGround *Ground ) { for( int row = originz; row <= originz + segmentcount; ++row ) { auto *cell = &Ground->Rects[ column ][ row ]; - - for( int subcellcolumn = 0; subcellcolumn < iNumSubRects; ++subcellcolumn ) { - for( int subcellrow = 0; subcellrow < iNumSubRects; ++subcellrow ) { - auto subcell = cell->FastGetSubRect( subcellcolumn, subcellrow ); - if( subcell == nullptr ) { continue; } - // renderowanie obiektów aktywnych a niewidocznych - for( auto node = subcell->nRenderHidden; node; node = node->nNext3 ) { - node->RenderHidden(); - } - // jeszcze dźwięki pojazdów by się przydały, również niewidocznych - subcell->RenderSounds(); - } - } - if( m_renderpass.camera.visible( cell->m_area ) ) { Render( cell ); } diff --git a/sound.cpp b/sound.cpp index f587239f..95f8c26b 100644 --- a/sound.cpp +++ b/sound.cpp @@ -259,28 +259,26 @@ void sound_manager::update(float dt) } } -void sound_manager::set_listener(glm::vec3 const &p, glm::vec3 const &at, glm::vec3 const &up) +void sound_manager::set_listener(glm::vec3 const &p, glm::mat3 const &r) { pos = p; - alListenerfv(AL_POSITION, glm::value_ptr(p)); + rot = r; + glm::vec3 at = glm::vec3(0.0, 0.0, -1.0) * r; + glm::vec3 up = glm::vec3(0.0, 1.0, 0.0) * r; + + alListenerfv(AL_POSITION, glm::value_ptr(pos)); glm::vec3 ori[] = { at, up }; alListenerfv(AL_ORIENTATION, (ALfloat*)ori); } -void sound_manager::set_listener(Math3D::vector3 const &p, Math3D::vector3 const &at, Math3D::vector3 const &up) -{ - set_listener((glm::vec3)glm::make_vec3(&p.x), (glm::vec3)glm::make_vec3(&at.x), (glm::vec3)glm::make_vec3(&up.x)); -} - sound::sound() { id = 0; alGenSources(1, &id); if (!id) throw std::runtime_error("sound: cannot generate source"); - alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); dist(5.0f * 3.82f); - spatial = false; + set_mode(global); gain_off = 0.0f; gain_mul = 1.0f; pitch_off = 0.0f; @@ -288,6 +286,18 @@ sound::sound() dt_sum = 0.0f; } +sound& sound::set_mode(mode_t m) +{ + mode = m; + + if (mode == global || mode == anchored) + alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); + else if (mode == spatial) + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + + return *this; +} + simple_sound::simple_sound(sound_buffer *buf) : sound::sound() { looping = false; @@ -308,22 +318,25 @@ simple_sound::~simple_sound() buffer->unref(); } -sound& sound::position(glm::vec3 const &p) +sound& sound::position(glm::vec3 p) { - if (!spatial) + if (mode == global) { - alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); - spatial = true; + set_mode(spatial); last_pos = p; } - if (p != pos) + if (p != pos || mode == anchored) { pos = p; pos_dirty = true; + if (mode == anchored) + p = (p - sound_man->pos) * glm::inverse(sound_man->rot); + alSourcefv(id, AL_POSITION, glm::value_ptr(p)); } + return *this; } @@ -343,7 +356,7 @@ sound& sound::dist(float dist) void simple_sound::play() { - if (playing || (spatial && glm::distance(pos, sound_man->pos) > max_dist)) + if (playing || (mode != global && glm::distance(pos, sound_man->pos) > max_dist)) return; alSourcePlay(id); @@ -361,9 +374,9 @@ void simple_sound::stop() void sound::update(float dt) { - if (spatial) + if (mode == spatial) dt_sum += dt; - if (spatial && pos_dirty) + if (mode == spatial && pos_dirty) { glm::vec3 velocity = (pos - last_pos) / dt_sum; // m/s alSourcefv(id, AL_VELOCITY, glm::value_ptr(velocity)); @@ -383,7 +396,7 @@ void simple_sound::update(float dt) alGetSourcei(id, AL_SOURCE_STATE, &v); if (v != AL_PLAYING) playing = false; - else if (spatial && glm::distance(pos, sound_man->pos) > max_dist) + else if (mode != global && glm::distance(pos, sound_man->pos) > max_dist) stop(); } } @@ -464,7 +477,7 @@ void complex_sound::play() if (cs != state::post) return; - if (spatial && glm::distance(pos, sound_man->pos) > max_dist) + if (mode != global && glm::distance(pos, sound_man->pos) > max_dist) return; alSourceRewind(id); @@ -554,7 +567,7 @@ void complex_sound::update(float dt) } if (cs == state::main) - if (spatial && glm::distance(pos, sound_man->pos) > max_dist) + if (mode != global && glm::distance(pos, sound_man->pos) > max_dist) { shut_by_dist = true; stop(); diff --git a/sound.h b/sound.h index 306dcb07..82f82556 100644 --- a/sound.h +++ b/sound.h @@ -45,9 +45,17 @@ class sound glm::vec3 last_pos; float dt_sum; +public: + enum mode_t + { + global, + spatial, + anchored + }; + protected: float max_dist; - bool spatial; + mode_t mode; glm::vec3 pos; int samplerate; @@ -68,12 +76,13 @@ public: virtual void stop() = 0; virtual void update(float dt); + sound& set_mode(mode_t); sound& dist(float); sound& gain(float); sound& pitch(float); virtual sound& loop(bool loop = true) = 0; - sound& position(glm::vec3 const &); + sound& position(glm::vec3); sound& position(Math3D::vector3 const &); }; @@ -155,6 +164,7 @@ class sound_manager public: glm::vec3 pos, last_pos; + glm::mat3 rot; sound_manager(); ~sound_manager(); @@ -166,8 +176,7 @@ public: void destroy_sound(sound**); void update(float dt); - void set_listener(glm::vec3 const &pos, glm::vec3 const &at, glm::vec3 const &up); - void set_listener(Math3D::vector3 const &pos, Math3D::vector3 const &at, Math3D::vector3 const &up); + void set_listener(glm::vec3 const &pos, glm::mat3 const &rot); }; extern std::unique_ptr sound_man; diff --git a/uart.cpp b/uart.cpp new file mode 100644 index 00000000..e3c70c23 --- /dev/null +++ b/uart.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include "uart.h" +#include "Globals.h" +#include "World.h" +#include "Train.h" +#include "Logs.h" + +uart_input::uart_input() +{ + conf = Global::uart_conf; + + if (sp_get_port_by_name(conf.port.c_str(), &port) != SP_OK) + throw std::runtime_error("uart: cannot find specified port"); + + if (sp_open(port, SP_MODE_READ_WRITE) != SP_OK) + throw std::runtime_error("uart: cannot open port"); + + if (sp_set_baudrate(port, conf.baud) != SP_OK) + throw std::runtime_error("uart: cannot set baudrate"); + + if (sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE) != SP_OK) + throw std::runtime_error("uart: cannot set flowcontrol"); + + if (sp_flush(port, SP_BUF_BOTH) != SP_OK) + throw std::runtime_error("uart: cannot flush"); + + old_packet.fill(0); + last_update = std::chrono::high_resolution_clock::now(); +} + +uart_input::~uart_input() +{ + sp_close(port); + sp_free_port(port); +} + +#define SPLIT_INT16(x) (uint8_t)x, (uint8_t)(x >> 8) + +void uart_input::poll() +{ + auto now = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration(now - last_update).count() < conf.updatetime) + return; + last_update = now; + + TTrain *t = Global::pWorld->train(); + + sp_return ret; + + if (sp_input_waiting(port) >= 16) + { + std::array buffer; + ret = sp_blocking_read(port, (void*)buffer.data(), buffer.size(), 0); + if (ret < 0) + throw std::runtime_error("uart: failed to read from port"); + + sp_drain(port); + data_pending = false; + + for (auto entry : input_bits) + { + input_type_t type = std::get<2>(entry); + + size_t byte = std::get<0>(entry) / 8; + size_t bit = std::get<0>(entry) % 8; + + bool state = (bool)(buffer[byte] & (1 << bit)); + + bool repeat = (type == impulse_r || + type == impulse_r_off || + type == impulse_r_on); + + bool changed = (state != (bool)(old_packet[byte] & (1 << bit))); + + if (!changed && !(repeat && state)) + continue; + + int action; + command_data::desired_state_t desired_state; + + if (type == toggle) + { + action = GLFW_PRESS; + desired_state = state ? command_data::ON : command_data::OFF; + } + else if (type == impulse_r_on) + { + action = state ? (changed ? GLFW_PRESS : GLFW_REPEAT) : GLFW_RELEASE; + desired_state = command_data::ON; + } + else if (type == impulse_r_off) + { + action = state ? (changed ? GLFW_PRESS : GLFW_REPEAT) : GLFW_RELEASE; + desired_state = command_data::OFF; + } + else if (type == impulse || type == impulse_r) + { + action = state ? (changed ? GLFW_PRESS : GLFW_REPEAT) : GLFW_RELEASE; + desired_state = command_data::TOGGLE; + } + + relay.post(std::get<1>(entry), 0, 0, action, 0, desired_state); + } + + int mainctrl = buffer[6]; + int scndctrl = buffer[7]; + float trainbrake = (float)(((uint16_t)buffer[8] | ((uint16_t)buffer[9] << 8)) - conf.mainbrakemin) / (conf.mainbrakemax - conf.mainbrakemin); + float localbrake = (float)(((uint16_t)buffer[10] | ((uint16_t)buffer[11] << 8)) - conf.mainbrakemin) / (conf.localbrakemax - conf.localbrakemin); + + t->set_mainctrl(mainctrl); + t->set_scndctrl(scndctrl); + t->set_trainbrake(trainbrake); + t->set_localbrake(localbrake); + + old_packet = buffer; + } + + if (!data_pending && sp_output_waiting(port) == 0) + { + // TODO: ugly! move it into structure like input_bits + + uint8_t buzzer = (uint8_t)t->get_alarm(); + uint8_t tacho = (uint8_t)t->get_tacho(); + uint16_t tank_press = (uint16_t)std::min(conf.tankuart, t->get_tank_pressure() / conf.tankmax * conf.tankuart); + uint16_t pipe_press = (uint16_t)std::min(conf.pipeuart, t->get_pipe_pressure() / conf.pipemax * conf.pipeuart); + uint16_t brake_press = (uint16_t)std::min(conf.brakeuart, t->get_brake_pressure() / conf.brakemax * conf.brakeuart); + uint16_t hv_voltage = (uint16_t)std::min(conf.hvuart, t->get_hv_voltage() / conf.hvmax * conf.hvuart); + auto current = t->get_current(); + uint16_t current1 = (uint16_t)std::min(conf.currentuart, current[0]) / conf.currentmax * conf.currentuart; + uint16_t current2 = (uint16_t)std::min(conf.currentuart, current[1]) / conf.currentmax * conf.currentuart; + uint16_t current3 = (uint16_t)std::min(conf.currentuart, current[2]) / conf.currentmax * conf.currentuart; + + std::array buffer = + { + 0, 0, //byte 0-1 + tacho, //byte 2 + 0, 0, 0, 0, 0, 0, //byte 3-8 + SPLIT_INT16(brake_press), //byte 9-10 + SPLIT_INT16(pipe_press), //byte 11-12 + SPLIT_INT16(tank_press), //byte 13-14 + SPLIT_INT16(hv_voltage), //byte 15-16 + SPLIT_INT16(current1), //byte 17-18 + SPLIT_INT16(current2), //byte 19-20 + SPLIT_INT16(current3), //byte 21-22 + 0, 0, 0, 0, 0, 0, 0, 0 //byte 23-30 + }; + + buffer[4] |= t->btLampkaOpory.b() << 1; + buffer[4] |= t->btLampkaWysRozr.b() << 2; + + buffer[6] |= t->btLampkaOgrzewanieSkladu.b() << 0; + buffer[6] |= t->btLampkaOpory.b() << 1; + buffer[6] |= t->btLampkaPoslizg.b() << 2; + buffer[6] |= t->btLampkaCzuwaka.b() << 6; + buffer[6] |= t->btLampkaSHP.b() << 7; + + buffer[7] |= t->btLampkaStyczn.b() << 0; + buffer[7] |= t->btLampkaNadmPrzetw.b() << 2; + buffer[7] |= t->btLampkaNadmSil.b() << 4; + buffer[7] |= t->btLampkaWylSzybki.b() << 5; + buffer[7] |= t->btLampkaNadmSpr.b() << 6; + + buffer[8] |= buzzer << 7; + + sp_flush(port, SP_BUF_INPUT); // flush input buffer in preparation for reply packet + + ret = sp_blocking_write(port, (void*)buffer.data(), buffer.size(), 0); + if (ret != buffer.size()) + throw std::runtime_error("uart: failed to write to port"); + + data_pending = true; + } +} diff --git a/uart.h b/uart.h new file mode 100644 index 00000000..e58faff4 --- /dev/null +++ b/uart.h @@ -0,0 +1,58 @@ +#pragma once +#include +#include "command.h" +#include "Globals.h" + +class uart_input +{ + enum input_type_t + { + toggle, + impulse, + impulse_r, + impulse_r_on, + impulse_r_off + }; + + std::array, 23> input_bits = + { + std::make_tuple(1, user_command::linebreakertoggle, impulse_r_off), + std::make_tuple(2, user_command::linebreakertoggle, impulse_r_on), + std::make_tuple(3, user_command::motoroverloadrelayreset, impulse), + std::make_tuple(5, user_command::converteroverloadrelayreset, impulse), + std::make_tuple(6, user_command::motorconnectorsopen, toggle), + std::make_tuple(7, user_command::alerteracknowledge, impulse_r), + + std::make_tuple(9, user_command::convertertoggle, toggle), + std::make_tuple(10, user_command::compressortoggle, toggle), + std::make_tuple(11, user_command::sandboxactivate, impulse), + std::make_tuple(12, user_command::heatingtoggle, toggle), + + std::make_tuple(15, user_command::motoroverloadrelaythresholdtoggle, toggle), + std::make_tuple(16, user_command::pantographtogglefront, toggle), + std::make_tuple(17, user_command::pantographtogglerear, toggle), + std::make_tuple(18, user_command::wheelspinbrakeactivate, impulse), + std::make_tuple(19, user_command::headlightsdimtoggle, toggle), + std::make_tuple(20, user_command::interiorlightdimtoggle, toggle), + std::make_tuple(21, user_command::independentbrakebailoff, impulse), + std::make_tuple(22, user_command::hornhighactivate, impulse), + std::make_tuple(23, user_command::hornlowactivate, impulse), + + std::make_tuple(24, user_command::batterytoggle, toggle), + std::make_tuple(25, user_command::headlighttoggleleft, toggle), + std::make_tuple(26, user_command::headlighttoggleupper, toggle), + std::make_tuple(27, user_command::headlighttoggleright, toggle) + }; + + sp_port *port = nullptr; + command_relay relay; + std::array old_packet; + std::chrono::time_point last_update; + Global::uart_conf_t conf; + bool data_pending = false; + +public: + uart_input(); + ~uart_input(); + void poll(); +}; diff --git a/version.h b/version.h index b11e1ac6..c063cca4 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 913 +#define VERSION_MINOR 914 #define VERSION_REVISION 0