feat: 增加了摄像头硬件复位和唤醒逻辑
This commit is contained in:
parent
c29c3652bc
commit
4e5dc91f10
|
@ -11,6 +11,7 @@ static class CameraAddr
|
||||||
public const UInt32 STORE_NUM = BASE + 0x13;
|
public const UInt32 STORE_NUM = BASE + 0x13;
|
||||||
public const UInt32 EXPECTED_VH = BASE + 0x14;
|
public const UInt32 EXPECTED_VH = BASE + 0x14;
|
||||||
public const UInt32 CAPTURE_ON = BASE + 0x15;
|
public const UInt32 CAPTURE_ON = BASE + 0x15;
|
||||||
|
public const UInt32 CAMERA_POWER = BASE + 0x16; //[0]: rstn, 0 is reset. [8]: power down, 1 is down.
|
||||||
}
|
}
|
||||||
|
|
||||||
class Camera
|
class Camera
|
||||||
|
@ -26,7 +27,7 @@ class Camera
|
||||||
|
|
||||||
const uint CAM_I2C_ADDR = 0x3C;
|
const uint CAM_I2C_ADDR = 0x3C;
|
||||||
const Peripherals.I2cClient.I2cProtocol CAM_PROTO = Peripherals.I2cClient.I2cProtocol.SCCB;
|
const Peripherals.I2cClient.I2cProtocol CAM_PROTO = Peripherals.I2cClient.I2cProtocol.SCCB;
|
||||||
const byte PLL_MUX = 10;
|
const byte PLL_MUX = 60;
|
||||||
const UInt32 FrameAddr = 0x00;
|
const UInt32 FrameAddr = 0x00;
|
||||||
|
|
||||||
// 动态分辨率参数
|
// 动态分辨率参数
|
||||||
|
@ -161,6 +162,56 @@ class Camera
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Result<bool>> EnableCameraHardware(bool isEnable)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.CAMERA_POWER, (isEnable ? 0x00000001u : 0x00000000u));
|
||||||
|
if (!ret.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to write STORE_ADDR: {ret.Error}");
|
||||||
|
return new(ret.Error);
|
||||||
|
}
|
||||||
|
if (!ret.Value)
|
||||||
|
{
|
||||||
|
logger.Error("STORE_ADDR write returned false");
|
||||||
|
return new(new Exception("STORE_ADDR write returned false"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Result<bool>> SleepCameraHardware(bool isEnable)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.CAMERA_POWER, (isEnable ? 0x00000101u : 0x00000001u));
|
||||||
|
if (!ret.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to write STORE_ADDR: {ret.Error}");
|
||||||
|
return new(ret.Error);
|
||||||
|
}
|
||||||
|
if (!ret.Value)
|
||||||
|
{
|
||||||
|
logger.Error("STORE_ADDR write returned false");
|
||||||
|
return new(new Exception("STORE_ADDR write returned false"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.Delay(5);
|
||||||
|
{
|
||||||
|
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.CAMERA_POWER, 0x00000101);
|
||||||
|
if (!ret.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to write STORE_ADDR: {ret.Error}");
|
||||||
|
return new(ret.Error);
|
||||||
|
}
|
||||||
|
if (!ret.Value)
|
||||||
|
{
|
||||||
|
logger.Error("STORE_ADDR write returned false");
|
||||||
|
return new(new Exception("STORE_ADDR write returned false"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 读取一帧图像数据
|
/// 读取一帧图像数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -177,7 +228,7 @@ class Camera
|
||||||
this.ep,
|
this.ep,
|
||||||
this.taskID, // taskID
|
this.taskID, // taskID
|
||||||
FrameAddr,
|
FrameAddr,
|
||||||
(int)(_currentWidth * _currentHeight * 2), // 使用当前分辨率的动态大小
|
(int)_currentFrameLength, // 使用当前分辨率的动态大小
|
||||||
this.timeout);
|
this.timeout);
|
||||||
|
|
||||||
if (!result.IsSuccessful)
|
if (!result.IsSuccessful)
|
||||||
|
@ -250,7 +301,6 @@ class Camera
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,7 @@ public class HttpVideoStreamService : BackgroundService
|
||||||
}
|
}
|
||||||
_cameraEnable = isEnabled;
|
_cameraEnable = isEnabled;
|
||||||
await _camera.EnableCamera(_cameraEnable);
|
await _camera.EnableCamera(_cameraEnable);
|
||||||
|
await _camera.SleepCameraHardware(!_cameraEnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -437,8 +437,11 @@ public class UDPClientPool
|
||||||
int outstanding = sentCount - (found.HasValue ? found.Value : 0);
|
int outstanding = sentCount - (found.HasValue ? found.Value : 0);
|
||||||
|
|
||||||
// If outstanding >= 512, wait for some data to be received
|
// If outstanding >= 512, wait for some data to be received
|
||||||
if (outstanding >= 512)
|
if (outstanding >= 512){
|
||||||
|
logger.Debug($"Outstanding data packets: {outstanding}, waiting for more data to be received...");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Send next address package
|
// Send next address package
|
||||||
var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(optsList[sentCount]));
|
var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(optsList[sentCount]));
|
||||||
|
@ -475,9 +478,9 @@ public class UDPClientPool
|
||||||
{
|
{
|
||||||
var bytes = udpDatas[i].Data;
|
var bytes = udpDatas[i].Data;
|
||||||
var expectedLen = ((optsList[i].BurstLength + 1) * 4);
|
var expectedLen = ((optsList[i].BurstLength + 1) * 4);
|
||||||
if (bytes.Length != expectedLen)
|
if ((bytes.Length - 4) != expectedLen)
|
||||||
return new(new Exception($"Expected {expectedLen} bytes but received {bytes.Length} bytes at segment {i}"));
|
return new(new Exception($"Expected {expectedLen} bytes but received {bytes.Length - 4} bytes at segment {i}"));
|
||||||
resultData.AddRange(bytes);
|
resultData.AddRange(bytes[4..]); // Skip the first 4 bytes (header)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate total data length
|
// Validate total data length
|
||||||
|
|
|
@ -314,19 +314,18 @@ export class VideoStreamClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置视频流分辨率
|
* 设置视频流分辨率
|
||||||
* @param width 宽度
|
* @param request 分辨率配置请求
|
||||||
* @param height 高度
|
* @return 设置结果
|
||||||
* @return 操作结果
|
|
||||||
*/
|
*/
|
||||||
setResolution(width: number, height: number): Promise<boolean> {
|
setResolution(request: ResolutionConfigRequest): Promise<any> {
|
||||||
let url_ = this.baseUrl + "/api/VideoStream/SetResolution";
|
let url_ = this.baseUrl + "/api/VideoStream/Resolution";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
const content_ = JSON.stringify({ width: width, height: height });
|
const content_ = JSON.stringify(request);
|
||||||
|
|
||||||
let options_: RequestInit = {
|
let options_: RequestInit = {
|
||||||
method: "POST",
|
|
||||||
body: content_,
|
body: content_,
|
||||||
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Accept": "application/json"
|
"Accept": "application/json"
|
||||||
|
@ -338,7 +337,7 @@ export class VideoStreamClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected processSetResolution(response: Response): Promise<boolean> {
|
protected processSetResolution(response: Response): Promise<any> {
|
||||||
const status = response.status;
|
const status = response.status;
|
||||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
|
@ -349,11 +348,20 @@ export class VideoStreamClient {
|
||||||
|
|
||||||
return result200;
|
return result200;
|
||||||
});
|
});
|
||||||
|
} else if (status === 400) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result400: any = null;
|
||||||
|
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||||
|
result400 = resultData400 !== undefined ? resultData400 : <any>null;
|
||||||
|
|
||||||
|
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
|
||||||
|
});
|
||||||
} else if (status === 500) {
|
} else if (status === 500) {
|
||||||
return response.text().then((_responseText) => {
|
return response.text().then((_responseText) => {
|
||||||
let result500: any = null;
|
let result500: any = null;
|
||||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||||
result500 = Exception.fromJS(resultData500);
|
result500 = resultData500 !== undefined ? resultData500 : <any>null;
|
||||||
|
|
||||||
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
||||||
});
|
});
|
||||||
} else if (status !== 200 && status !== 204) {
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
@ -361,15 +369,15 @@ export class VideoStreamClient {
|
||||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.resolve<boolean>(null as any);
|
return Promise.resolve<any>(null as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前视频流分辨率
|
* 获取当前分辨率
|
||||||
* @return 当前分辨率信息
|
* @return 当前分辨率信息
|
||||||
*/
|
*/
|
||||||
getCurrentResolution(): Promise<any> {
|
getCurrentResolution(): Promise<any> {
|
||||||
let url_ = this.baseUrl + "/api/VideoStream/GetCurrentResolution";
|
let url_ = this.baseUrl + "/api/VideoStream/Resolution";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
let options_: RequestInit = {
|
let options_: RequestInit = {
|
||||||
|
@ -399,7 +407,8 @@ export class VideoStreamClient {
|
||||||
return response.text().then((_responseText) => {
|
return response.text().then((_responseText) => {
|
||||||
let result500: any = null;
|
let result500: any = null;
|
||||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||||
result500 = Exception.fromJS(resultData500);
|
result500 = resultData500 !== undefined ? resultData500 : <any>null;
|
||||||
|
|
||||||
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
||||||
});
|
});
|
||||||
} else if (status !== 200 && status !== 204) {
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
@ -411,11 +420,11 @@ export class VideoStreamClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取支持的视频流分辨率列表
|
* 获取支持的分辨率列表
|
||||||
* @return 支持的分辨率列表
|
* @return 支持的分辨率列表
|
||||||
*/
|
*/
|
||||||
getSupportedResolutions(): Promise<any[]> {
|
getSupportedResolutions(): Promise<any> {
|
||||||
let url_ = this.baseUrl + "/api/VideoStream/GetSupportedResolutions";
|
let url_ = this.baseUrl + "/api/VideoStream/SupportedResolutions";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
let options_: RequestInit = {
|
let options_: RequestInit = {
|
||||||
|
@ -430,7 +439,7 @@ export class VideoStreamClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected processGetSupportedResolutions(response: Response): Promise<any[]> {
|
protected processGetSupportedResolutions(response: Response): Promise<any> {
|
||||||
const status = response.status;
|
const status = response.status;
|
||||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
|
@ -445,7 +454,8 @@ export class VideoStreamClient {
|
||||||
return response.text().then((_responseText) => {
|
return response.text().then((_responseText) => {
|
||||||
let result500: any = null;
|
let result500: any = null;
|
||||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||||
result500 = Exception.fromJS(resultData500);
|
result500 = resultData500 !== undefined ? resultData500 : <any>null;
|
||||||
|
|
||||||
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
||||||
});
|
});
|
||||||
} else if (status !== 200 && status !== 204) {
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
@ -453,7 +463,7 @@ export class VideoStreamClient {
|
||||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.resolve<any[]>(null as any);
|
return Promise.resolve<any>(null as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2931,6 +2941,52 @@ export interface ICameraConfigRequest {
|
||||||
port: number;
|
port: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 分辨率配置请求模型 */
|
||||||
|
export class ResolutionConfigRequest implements IResolutionConfigRequest {
|
||||||
|
/** 宽度 */
|
||||||
|
width!: number;
|
||||||
|
/** 高度 */
|
||||||
|
height!: number;
|
||||||
|
|
||||||
|
constructor(data?: IResolutionConfigRequest) {
|
||||||
|
if (data) {
|
||||||
|
for (var property in data) {
|
||||||
|
if (data.hasOwnProperty(property))
|
||||||
|
(<any>this)[property] = (<any>data)[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_data?: any) {
|
||||||
|
if (_data) {
|
||||||
|
this.width = _data["width"];
|
||||||
|
this.height = _data["height"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJS(data: any): ResolutionConfigRequest {
|
||||||
|
data = typeof data === 'object' ? data : {};
|
||||||
|
let result = new ResolutionConfigRequest();
|
||||||
|
result.init(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(data?: any) {
|
||||||
|
data = typeof data === 'object' ? data : {};
|
||||||
|
data["width"] = this.width;
|
||||||
|
data["height"] = this.height;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分辨率配置请求模型 */
|
||||||
|
export interface IResolutionConfigRequest {
|
||||||
|
/** 宽度 */
|
||||||
|
width: number;
|
||||||
|
/** 高度 */
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class ProblemDetails implements IProblemDetails {
|
export class ProblemDetails implements IProblemDetails {
|
||||||
type?: string | undefined;
|
type?: string | undefined;
|
||||||
title?: string | undefined;
|
title?: string | undefined;
|
||||||
|
|
|
@ -340,7 +340,7 @@ import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
} from "lucide-vue-next";
|
} from "lucide-vue-next";
|
||||||
import { VideoStreamClient, CameraConfigRequest } from "@/APIClient";
|
import { VideoStreamClient, CameraConfigRequest, ResolutionConfigRequest } from "@/APIClient";
|
||||||
import { useEquipments } from "@/stores/equipments";
|
import { useEquipments } from "@/stores/equipments";
|
||||||
|
|
||||||
const eqps = useEquipments();
|
const eqps = useEquipments();
|
||||||
|
@ -597,7 +597,8 @@ const refreshResolutions = async () => {
|
||||||
try {
|
try {
|
||||||
addLog("info", "正在获取支持的分辨率列表...");
|
addLog("info", "正在获取支持的分辨率列表...");
|
||||||
const resolutions = await videoClient.getSupportedResolutions();
|
const resolutions = await videoClient.getSupportedResolutions();
|
||||||
supportedResolutions.value = resolutions;
|
supportedResolutions.value = resolutions.resolutions;
|
||||||
|
console.log("支持的分辨率列表:", supportedResolutions.value);
|
||||||
|
|
||||||
// 获取当前分辨率
|
// 获取当前分辨率
|
||||||
const currentRes = await videoClient.getCurrentResolution();
|
const currentRes = await videoClient.getCurrentResolution();
|
||||||
|
@ -629,7 +630,11 @@ const changeResolution = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置新分辨率
|
// 设置新分辨率
|
||||||
const success = await videoClient.setResolution(selectedResolution.value.width, selectedResolution.value.height);
|
const resolutionRequest = new ResolutionConfigRequest({
|
||||||
|
width: selectedResolution.value.width,
|
||||||
|
height: selectedResolution.value.height
|
||||||
|
});
|
||||||
|
const success = await videoClient.setResolution(resolutionRequest);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// 刷新流信息
|
// 刷新流信息
|
||||||
|
|
Loading…
Reference in New Issue