Загрузка данных


#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <ctime>

using namespace std;

const int NUM_SHIPS = 2;
const int SHIP_CAPACITY = 5;

const int MIN_SAIL_TIME = 3000;
const int MAX_SAIL_TIME = 6000;

const int MIN_PATIENCE = 2000;
const int MAX_PATIENCE = 7000;

const int MIN_ARRIVAL_DELAY = 200;
const int MAX_ARRIVAL_DELAY = 800;

const wchar_t* PIPE_NAME = L"\\\\.\\pipe\\PilgrimagePipe";
const wchar_t* CLIENT_EXE = L"ClientPilgrimagePipe.exe";

// ===================== СТРУКТУРЫ ОБМЕНА =====================
struct PilgrimRequest
{
    int id;
    int patienceMs;
};

struct ServerReply
{
    int status;      // 0 = FOOT, 1 = SHIP
    int shipId;
    int placeNumber;
};

// ===================== ГЛОБАЛЬНЫЕ ОБЪЕКТЫ =====================
HANDLE gMutex;
HANDLE gFinishEvent;

// ===================== СТАТИСТИКА =====================
int gTotalPilgrims = 0;
int gByShip = 0;
int gOnFoot = 0;

// ===================== КОРАБЛЬ =====================
struct Ship
{
    int id;
    int passengers;
    bool inPort;
    int voyages;
    HANDLE fullEvent;
};

Ship gShips[NUM_SHIPS];

// ===================== ДАННЫЕ ДЛЯ ПОТОКА КЛИЕНТА =====================
struct ClientContext
{
    HANDLE hPipe;
};

// ===================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =====================
void print_time()
{
    SYSTEMTIME lt;
    GetLocalTime(&lt);
    printf("%02d:%02d:%02d\t", lt.wHour, lt.wMinute, lt.wSecond);
}

int RandomRange(int left, int right)
{
    return left + rand() % (right - left + 1);
}

void SafePrint(const string& text)
{
    WaitForSingleObject(gMutex, INFINITE);
    cout << text << endl;
    ReleaseMutex(gMutex);
}

bool LaunchClientProcess(int pilgrimId, int patienceMs)
{
    wstring cmd = wstring(CLIENT_EXE) + L" " +
                  to_wstring(pilgrimId) + L" " +
                  to_wstring(patienceMs);

    vector<wchar_t> cmdBuffer(cmd.begin(), cmd.end());
    cmdBuffer.push_back(L'\0');

    STARTUPINFOW si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    BOOL ok = CreateProcessW(
        NULL,
        cmdBuffer.data(),
        NULL,
        NULL,
        FALSE,
        CREATE_NEW_CONSOLE,
        NULL,
        NULL,
        &si,
        &pi
    );

    if (!ok)
    {
        print_time();
        wcout << L"[ERROR] Не удалось запустить клиента #" << pilgrimId
              << L". Код: " << GetLastError() << endl;
        return false;
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return true;
}

// ===================== ПОТОК КОРАБЛЯ =====================
DWORD WINAPI ShipThread(LPVOID lpParam)
{
    Ship* ship = (Ship*)lpParam;

    HANDLE waitHandles[2];
    waitHandles[0] = ship->fullEvent;
    waitHandles[1] = gFinishEvent;

    while (true)
    {
        WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);

        WaitForSingleObject(gMutex, INFINITE);

        bool finishSignaled = (WaitForSingleObject(gFinishEvent, 0) == WAIT_OBJECT_0);

        if (finishSignaled && ship->passengers == 0)
        {
            ReleaseMutex(gMutex);
            print_time();
            cout << "[SHIP] Корабль #" << ship->id << " завершает работу.\n";
            return 0;
        }

        if (ship->passengers == 0)
        {
            ResetEvent(ship->fullEvent);
            ReleaseMutex(gMutex);
            continue;
        }

        int currentPassengers = ship->passengers;
        ship->inPort = false;

        print_time();
        if (finishSignaled && currentPassengers < SHIP_CAPACITY)
        {
            cout << "[SHIP] Корабль #" << ship->id
                 << " выполняет последний рейс с "
                 << currentPassengers << " паломниками.\n";
        }
        else
        {
            cout << "[SHIP] Корабль #" << ship->id
                 << " заполнен (" << currentPassengers
                 << " паломников) и отплывает.\n";
        }

        ReleaseMutex(gMutex);

        Sleep(RandomRange(MIN_SAIL_TIME, MAX_SAIL_TIME));

        WaitForSingleObject(gMutex, INFINITE);

        ship->voyages++;
        ship->passengers = 0;
        ship->inPort = true;
        ResetEvent(ship->fullEvent);

        print_time();
        cout << "[SHIP] Корабль #" << ship->id
             << " вернулся. Рейсов: " << ship->voyages << ".\n";

        ReleaseMutex(gMutex);
    }
}

// ===================== ПОТОК ОБСЛУЖИВАНИЯ КЛИЕНТА =====================
DWORD WINAPI ClientThread(LPVOID lpParam)
{
    ClientContext* ctx = (ClientContext*)lpParam;
    HANDLE hPipe = ctx->hPipe;

    PilgrimRequest request{};
    DWORD cbRead = 0;

    BOOL fSuccess = ReadFile(
        hPipe,
        &request,
        sizeof(PilgrimRequest),
        &cbRead,
        NULL
    );

    if (!fSuccess || cbRead != sizeof(PilgrimRequest))
    {
        print_time();
        cout << "[ERROR] ReadFile failed. GLE=" << GetLastError() << '\n';
        FlushFileBuffers(hPipe);
        DisconnectNamedPipe(hPipe);
        CloseHandle(hPipe);
        delete ctx;
        return 1;
    }

    int pilgrimId = request.id;
    int patienceMs = request.patienceMs;

    WaitForSingleObject(gMutex, INFINITE);
    print_time();
    cout << "[CLIENT] Паломник #" << pilgrimId
         << " прибыл в порт. Терпение: "
         << patienceMs << " мс.\n";
    ReleaseMutex(gMutex);

    DWORD startTime = GetTickCount();
    ServerReply reply{};
    bool decided = false;

    while (!decided)
    {
        bool boarded = false;

        WaitForSingleObject(gMutex, INFINITE);

        for (int i = 0; i < NUM_SHIPS; i++)
        {
            if (gShips[i].inPort && gShips[i].passengers < SHIP_CAPACITY)
            {
                gShips[i].passengers++;
                int placeNumber = gShips[i].passengers;

                gByShip++;
                boarded = true;

                reply.status = 1;
                reply.shipId = gShips[i].id;
                reply.placeNumber = placeNumber;

                print_time();
                cout << "[CLIENT] Паломник #" << pilgrimId
                     << " сел на корабль #" << gShips[i].id
                     << ". Место " << placeNumber
                     << "/" << SHIP_CAPACITY << ".\n";

                if (gShips[i].passengers == SHIP_CAPACITY)
                {
                    SetEvent(gShips[i].fullEvent);
                }

                break;
            }
        }

        ReleaseMutex(gMutex);

        if (boarded)
        {
            decided = true;
            break;
        }

        DWORD waited = GetTickCount() - startTime;
        if (waited >= (DWORD)patienceMs)
        {
            WaitForSingleObject(gMutex, INFINITE);

            gOnFoot++;
            reply.status = 0;
            reply.shipId = 0;
            reply.placeNumber = 0;

            print_time();
            cout << "[CLIENT] Паломник #" << pilgrimId
                 << " не дождался корабля и ушел пешком.\n";

            ReleaseMutex(gMutex);

            decided = true;
            break;
        }

        Sleep(100);
    }

    DWORD cbWritten = 0;
    fSuccess = WriteFile(
        hPipe,
        &reply,
        sizeof(ServerReply),
        &cbWritten,
        NULL
    );

    if (!fSuccess || cbWritten != sizeof(ServerReply))
    {
        print_time();
        cout << "[ERROR] WriteFile failed. GLE=" << GetLastError() << '\n';
    }

    FlushFileBuffers(hPipe);
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);
    delete ctx;

    return 0;
}

// ===================== MAIN =====================
int main()
{
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);

    srand(static_cast<unsigned>(time(nullptr)));

    gMutex = CreateMutex(NULL, FALSE, NULL);
    if (gMutex == NULL)
    {
        print_time();
        cout << "[ERROR] CreateMutex failed. GLE=" << GetLastError() << '\n';
        return 1;
    }

    gFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (gFinishEvent == NULL)
    {
        print_time();
        cout << "[ERROR] CreateEvent(gFinishEvent) failed. GLE=" << GetLastError() << '\n';
        CloseHandle(gMutex);
        return 1;
    }

    for (int i = 0; i < NUM_SHIPS; i++)
    {
        gShips[i].id = i + 1;
        gShips[i].passengers = 0;
        gShips[i].inPort = true;
        gShips[i].voyages = 0;
        gShips[i].fullEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        if (gShips[i].fullEvent == NULL)
        {
            print_time();
            cout << "[ERROR] CreateEvent(ship fullEvent) failed. GLE=" << GetLastError() << '\n';
            return 1;
        }
    }

    HANDLE shipThreads[NUM_SHIPS];
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        shipThreads[i] = CreateThread(NULL, 0, ShipThread, &gShips[i], 0, NULL);
        if (shipThreads[i] == NULL)
        {
            print_time();
            cout << "[ERROR] CreateThread(ship) failed. GLE=" << GetLastError() << '\n';
            return 1;
        }
    }

    int n;
    cout << "Введите количество паломников (1..20): ";
    cin >> n;

    while (cin.fail() || n < 1 || n > 20 || cin.peek() != '\n')
    {
        cin.clear();
        cin.ignore(32768, '\n');
        cout << "Некорректный ввод. Введите количество паломников (1..20): ";
        cin >> n;
    }

    gTotalPilgrims = n;

    print_time();
    cout << "[MESSAGE] Сервер запущен. Запускаю клиентские процессы...\n";

    for (int i = 0; i < n; i++)
    {
        int patience = RandomRange(MIN_PATIENCE, MAX_PATIENCE);
        LaunchClientProcess(i + 1, patience);
        Sleep(RandomRange(MIN_ARRIVAL_DELAY, MAX_ARRIVAL_DELAY));
    }

    vector<HANDLE> clientThreads;

    for (int i = 0; i < n; i++)
    {
        HANDLE hPipe = CreateNamedPipeW(
            PIPE_NAME,
            PIPE_ACCESS_DUPLEX,
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
            n,
            sizeof(ServerReply),
            sizeof(PilgrimRequest),
            5000,
            NULL
        );

        if (hPipe == INVALID_HANDLE_VALUE)
        {
            print_time();
            cout << "[ERROR] CreateNamedPipe failed. GLE=" << GetLastError() << '\n';
            continue;
        }

        BOOL fConnected = ConnectNamedPipe(hPipe, NULL) ?
                          TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

        if (!fConnected)
        {
            print_time();
            cout << "[ERROR] ConnectNamedPipe failed. GLE=" << GetLastError() << '\n';
            CloseHandle(hPipe);
            continue;
        }

        ClientContext* ctx = new ClientContext;
        ctx->hPipe = hPipe;

        HANDLE hClientThread = CreateThread(NULL, 0, ClientThread, ctx, 0, NULL);
        if (hClientThread == NULL)
        {
            print_time();
            cout << "[ERROR] CreateThread(client) failed. GLE=" << GetLastError() << '\n';
            DisconnectNamedPipe(hPipe);
            CloseHandle(hPipe);
            delete ctx;
            continue;
        }

        clientThreads.push_back(hClientThread);
    }

    if (!clientThreads.empty())
    {
        WaitForMultipleObjects((DWORD)clientThreads.size(), clientThreads.data(), TRUE, INFINITE);
    }

    print_time();
    cout << "[MESSAGE] Все паломники определились со способом пути.\n";

    WaitForSingleObject(gMutex, INFINITE);
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        if (gShips[i].passengers > 0)
        {
            SetEvent(gShips[i].fullEvent);
        }
    }
    ReleaseMutex(gMutex);

    SetEvent(gFinishEvent);

    for (int i = 0; i < NUM_SHIPS; i++)
    {
        SetEvent(gShips[i].fullEvent);
    }

    WaitForMultipleObjects(NUM_SHIPS, shipThreads, TRUE, INFINITE);

    cout << "\n========== ИТОГ ==========\n";
    cout << "Всего паломников: " << gTotalPilgrims << endl;
    cout << "Добрались кораблем: " << gByShip << endl;
    cout << "Ушли пешком: " << gOnFoot << endl;
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        cout << "Корабль #" << gShips[i].id
             << " совершил рейсов: " << gShips[i].voyages << endl;
    }
    cout << "==========================\n";

    for (HANDLE h : clientThreads)
        CloseHandle(h);

    for (int i = 0; i < NUM_SHIPS; i++)
    {
        CloseHandle(shipThreads[i]);
        CloseHandle(gShips[i].fullEvent);
    }

    CloseHandle(gFinishEvent);
    CloseHandle(gMutex);

    return 0;
}