Перейти к содержанию
Форум Радиодар

RDVCR - программа для записи сборки заказов интернет магазина "Радиодар"


Рекомендуемые сообщения

Опубликовано

Постоянные поиски видеорегистраторов для нашего отдела сборки заказов были омрачены дополнительными сложностями в виде новых кодировщиков стандарта H.265. Старые версии ПО для видеозаписей на сервер поддерживали только 264 версию, и после приобретения камер высокого разрешения 4к встал вопрос: покупать новые версии программ видеорегистрации, либо написать самому. 

Решили сами сделать так, как нам будет удобно, да и в случае обновления стандартов и протоколов будет легко адаптировать её.

Основные требования: 

1) Всегда поверх всех окон - оператор не должен закрывать, сворачивать либо убирать окно записи, программа всегда должна быть на виду сборщика;

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

3) После нажатия "СТАРТ ЗАПИСИ" должно быть окно контроля действий сборщика, а также броская мигающая надпись о текущем процессе записи;

4) После окончания записи видеофайл должен сохраняться на выбранный диск с названием, содержащим номер заказа + дату файла. После сохранения необходимо проверить успешность выгрузки файла, если все отлично - выдать уведомление. Затем стереть содержимое поля ввода номера заказа для подготовки ввода следующего номера;

5) Дополнительно должны проверяться все необходимые зависимости и обрабатываться возможные ошибки с выводом информации об ошибке:

5.1 - не установлен VLC x32

5.2 - нет второго диска для сохранения файлов

 

Решение задачи:

Используем бесплатный стек Visual Studio под приложение .NET для Windows desktop. Создадим новый проект, выбираем .NET Desktop Development (Разработка классических приложений .NET) - он необходим для создания Windows Forms.

Далее открываем Form1 и указываем код (ver.3.1):

using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Vlc.DotNet.Forms;
using System.Runtime.InteropServices;

namespace RecordingApp
{
    public partial class Form1 : Form
    {
        private System.Windows.Forms.Timer blinkTimer;
        private Panel statusPanel;
        private Label lblStatus;
        private VlcControl vlcControl;
        private TextBox txtOrderNumber;
        private Button btnStart;
        private Button btnStop;
        private bool isRecording = false;

        public Form1()
        {
            CreateUI();
            blinkTimer = new System.Windows.Forms.Timer { Interval = 500 };
            blinkTimer.Tick += BlinkTimer_Tick;
            blinkTimer.Start(); // Запускаем сразу для удержания TopMost
        }
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        const uint SWP_NOSIZE = 0x0001;
        const uint SWP_NOMOVE = 0x0002;
        const uint TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

        private void CreateUI()
        {
            this.Text = "Контроль сборки заказов";
            this.Size = new Size(900, 700);
            // 1. Всегда поверх всех окон
            this.TopMost = true;
            this.TopMost = false;
            this.TopMost = true;
            this.WindowState = FormWindowState.Normal;

            // 2. Убираем кнопки Свернуть, Развернуть и Закрыть (крестик)
            this.ControlBox = false;

            // 3. Запрещаем изменять размер окна (опционально, для красоты)
            this.FormBorderStyle = FormBorderStyle.FixedSingle;

            this.StartPosition = FormStartPosition.CenterScreen;
            this.BackColor = Color.FromArgb(240, 240, 240);

            Panel topPanel = new Panel { Dock = DockStyle.Top, Height = 80, BackColor = Color.FromArgb(220, 220, 220) };
            Label lblOrder = new Label { Text = "Номер заказа:", Location = new Point(20, 28), Size = new Size(130, 25), Font = new Font("Segoe UI", 11F, FontStyle.Bold) };
            txtOrderNumber = new TextBox { Location = new Point(160, 25), Size = new Size(250, 27), Font = new Font("Segoe UI", 11F), TextAlign = HorizontalAlignment.Center };

            btnStart = new Button { Text = "▶ СТАРТ ЗАПИСИ", Location = new Point(430, 20), Size = new Size(170, 40), BackColor = Color.LightGreen, Font = new Font("Segoe UI", 11F, FontStyle.Bold), Enabled = false };
            btnStart.Click += BtnStart_Click;

            btnStop = new Button { Text = "⏹ СТОП ЗАПИСИ", Location = new Point(620, 20), Size = new Size(170, 40), BackColor = Color.LightCoral, Font = new Font("Segoe UI", 11F, FontStyle.Bold), Enabled = false };
            btnStop.Click += BtnStop_Click;

            txtOrderNumber.TextChanged += (s, e) => btnStart.Enabled = !string.IsNullOrWhiteSpace(txtOrderNumber.Text) && !isRecording;

            topPanel.Controls.AddRange(new Control[] { lblOrder, txtOrderNumber, btnStart, btnStop });

            statusPanel = new Panel { Dock = DockStyle.Top, Height = 70, BackColor = Color.DarkRed, Visible = false };
            lblStatus = new Label { Text = "", Font = new Font("Segoe UI", 20F, FontStyle.Bold), ForeColor = Color.White, TextAlign = ContentAlignment.MiddleCenter, Dock = DockStyle.Fill };
            statusPanel.Controls.Add(lblStatus);

            vlcControl = new VlcControl { Dock = DockStyle.Fill, BackColor = Color.Black };
            ((System.ComponentModel.ISupportInitialize)vlcControl).BeginInit();
            vlcControl.VlcLibDirectoryNeeded += OnVlcLibDirectoryNeeded;
            ((System.ComponentModel.ISupportInitialize)vlcControl).EndInit();

            this.Controls.Add(vlcControl);
            this.Controls.Add(statusPanel);
            this.Controls.Add(topPanel);
        }

        private void OnVlcLibDirectoryNeeded(object sender, VlcLibDirectoryNeededEventArgs e)
        {
            string vlcPath = @"C:\Program Files (x86)\VideoLAN\VLC";
            if (!Directory.Exists(vlcPath))
            {
                MessageBox.Show("Установите 32-битную версию VLC!", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            e.VlcLibDirectory = new DirectoryInfo(vlcPath);
        }

        private void BtnStart_Click(object sender, EventArgs e)
        {
            string orderNumber = txtOrderNumber.Text.Trim();
            string folderPath = @"D:\Video";

            try
            {
                if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);

                string fileName = $"Order_{orderNumber}_{DateTime.Now:yyyyMMdd_HHmmss}.mkv";
                string fullPath = Path.Combine(folderPath, fileName).Replace("\\", "/");
                string cameraUrl = "rtsp://admin:admin@192.168.0.20:554/Streaming/Channels/101";

                // Дублируем поток H.265 в файл MKV и на экран без перекодирования
                var mediaOptions = new[]
                {
                    $":sout=#duplicate{{dst=display,dst=std{{access=file,mux=ffmpeg{{mux=matroska}},dst='{fullPath}'}}}}",
                    ":sout-keep"
                };

                vlcControl.Stop();
                vlcControl.Play(new Uri(cameraUrl), mediaOptions);

                isRecording = true;
                btnStart.Enabled = false;
                btnStop.Enabled = true;
                txtOrderNumber.Enabled = false;
                statusPanel.Visible = true;
                lblStatus.Text = $"⚠ ЗАПИСЬ ЗАКАЗА № {orderNumber} ⚠";
                blinkTimer.Start();
                this.Text = $"Запись заказа №{orderNumber}";
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка: {ex.Message}");
            }
            SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
        }

        private void BtnStop_Click(object sender, EventArgs e)
        {
            try
            {
                vlcControl.Stop();
                isRecording = false;
                btnStop.Enabled = false;
                txtOrderNumber.Enabled = true;
                txtOrderNumber.Clear();
                statusPanel.Visible = false;
                blinkTimer.Stop();
                this.Text = "Контроль сборки заказов";
                MessageBox.Show("Запись заказа успешно завершена и сохранена.", "Готово", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка при остановке: {ex.Message}");
            }
        }

        private void BlinkTimer_Tick(object sender, EventArgs e)
        {
            statusPanel.BackColor = (statusPanel.BackColor == Color.DarkRed) ? Color.Crimson : Color.DarkRed;
            lblStatus.ForeColor = (lblStatus.ForeColor == Color.White) ? Color.Yellow : Color.White;

            // ПРИНУДИТЕЛЬНО возвращаем окно в самый верх каждые 0.5 сек
            // Это не даст браузеру перекрыть программу во время работы
            SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
        }
    }
}

 

В итоге получилась простенькая программа:

image.png

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйте новый аккаунт в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
  • Статистика пользователей

    464
    Пользователей
    681
    Максимум онлайн
    Glennisorm
    Новый пользователь
    Glennisorm
    Регистрация
×
×
  • Создать...