C# Grabber Service

選取GrabberService專案按下滑鼠右鍵並選取管理NuGet套件

安裝Newtonsoft.Json套件

專案新增appsettings.json

選取GrabberService專案按下滑鼠右鍵並選取"加入",接著選取"新增項目"

輸入名稱: appsettings.json

滑鼠雙擊開啟appsettings.json

GrabberService/FrameHeader.cs

using System.Runtime.InteropServices;
namespace GrabberService;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct FrameHeader
{
    public ulong FrameId;
    public ulong TimestampNs;
    public uint Width;
    public uint Height;
    public uint Stride;
    public uint PixelFormat;
    public uint ImageSize;
    public uint CameraIndex;
    public uint Flags;
}

GrabberService/SharedMemoryPublisher.cs

using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

namespace GrabberService;

public sealed class SharedMemoryPublisher : IDisposable
{
    private readonly string _mapName;
    private readonly int _capacity;
    private readonly MemoryMappedFile _mmf;
    private readonly MemoryMappedViewAccessor _accessor;
    private readonly object _lock = new();

    public SharedMemoryPublisher(string mapName, int capacity)
    {
        _mapName = mapName;
        _capacity = capacity;
        _mmf = MemoryMappedFile.CreateOrOpen(_mapName, _capacity, MemoryMappedFileAccess.ReadWrite);
        _accessor = _mmf.CreateViewAccessor(0, _capacity, MemoryMappedFileAccess.ReadWrite);
    }

    public void Publish(FrameHeader header, byte[] image)
    {
        int headerSize = Marshal.SizeOf<FrameHeader>();
        if (headerSize + image.Length > _capacity)
        {
            throw new InvalidOperationException("Shared memory capacity is too small.");
        }

        lock (_lock)
        {
            _accessor.Write(0, ref header);
            _accessor.WriteArray(headerSize, image, 0, image.Length);
        }
    }

    public void Dispose()
    {
        _accessor.Dispose();
        _mmf.Dispose();
    }
}

GrabberService/CameraSimulator.cs

namespace GrabberService;

public sealed class CameraSimulator
{
    private readonly int _width;
    private readonly int _height;
    private ulong _frameId = 0;
    private int _tick = 0;

    public CameraSimulator(int width, int height)
    {
        _width = width;
        _height = height;
    }

    public (FrameHeader header, byte[] image) Grab()
    {
        byte[] img = new byte[_width * _height];

        int cx = (_tick * 7) % _width;
        int cy = (_tick * 5) % _height;

        for (int y = 0; y < _height; y++)
        {
            for (int x = 0; x < _width; x++)
            {
                int idx = y * _width + x;
                int dx = x - cx;
                int dy = y - cy;
                int r2 = dx * dx + dy * dy;

                byte v = (byte)((x + y + _tick) % 256);
                if (r2 < 30 * 30)
                    v = 255;

                img[idx] = v;
            }
        }

        var header = new FrameHeader
        {
            FrameId = ++_frameId,
            TimestampNs = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000UL,
            Width = (uint)_width,
            Height = (uint)_height,
            Stride = (uint)_width,
            PixelFormat = 1,
            ImageSize = (uint)img.Length,
            CameraIndex = 0,
            Flags = 0
        };

        _tick++;
        return (header, img);
    }
}

必裝 NuGet 套件

GrabberService 專案 → 右鍵 → 管理 NuGet 套件

搜尋並安裝:NetMQ

搜尋並安裝:Microsoft.Extensions.Hosting

搜尋並安裝:Microsoft.Extensions.Logging

GrabberService/CommandServer.cs

using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using NetMQ;
using NetMQ.Sockets;

namespace GrabberService;

public sealed class CommandServer : IDisposable
{
    private readonly string _bindAddress;
    private readonly ILogger<CommandServer> _logger;
    private readonly ResponseSocket _socket;

    public bool IsGrabbing { get; private set; } = false;

    public CommandServer(string bindAddress, ILogger<CommandServer> logger)
    {
        _bindAddress = bindAddress;
        _logger = logger;

        AsyncIO.ForceDotNet.Force();
        _socket = new ResponseSocket();
        _socket.Bind(_bindAddress);
    }

    public bool PollOnce()
    {
        if (!_socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(1), out var msg))
            return false;

        Dictionary<string, object?> response;

        try
        {
            var req = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(msg)
                      ?? new Dictionary<string, JsonElement>();

            var cmd = req.TryGetValue("cmd", out var cmdEl)
                ? cmdEl.GetString()
                : string.Empty;

            response = cmd switch
            {
                "start" => StartResponse(),
                "stop" => StopResponse(),
                "status" => StatusResponse(),
                "heartbeat" => Ok("alive"),
                _ => Error($"Unknown command: {cmd}")
            };
        }
        catch (Exception ex)
        {
            response = Error(ex.Message);
        }

        _socket.SendFrame(JsonSerializer.Serialize(response));
        return true;
    }

    private Dictionary<string, object?> StartResponse()
    {
        IsGrabbing = true;
        _logger.LogInformation("Grab started");

        return Ok("started");
    }

    private Dictionary<string, object?> StopResponse()
    {
        IsGrabbing = false;
        _logger.LogInformation("Grab stopped");

        return Ok("stopped");
    }

    private Dictionary<string, object?> StatusResponse()
    {
        return new Dictionary<string, object?>
        {
            ["ok"] = true,
            ["grabbing"] = IsGrabbing
        };
    }

    private static Dictionary<string, object?> Ok(string message)
    {
        return new Dictionary<string, object?>
        {
            ["ok"] = true,
            ["message"] = message
        };
    }

    private static Dictionary<string, object?> Error(string message)
    {
        return new Dictionary<string, object?>
        {
            ["ok"] = false,
            ["message"] = message
        };
    }

    public void Dispose()
    {
        _socket?.Dispose();
        NetMQConfig.Cleanup();
    }
}

















文章標籤
全站熱搜
創作者介紹
創作者 me1237guy 的頭像
me1237guy

天天向上

me1237guy 發表在 痞客邦 留言(0) 人氣(3)