、    

 

C#通过SIP协议实现对网络广播音柱的声音播放

1、电脑端下载并安装FreeSWITCH,配置教程可千问、豆包,防火墙端口要配置正确SIP(5506),FSL(8821),RTP(16384-32768)

2、网络广播音柱配置

参数填写FreeSwitch端分配的帐户信息

网络广播音柱SIP配置

音频编码PCMA或者PCMU(站长用的是PCMU)

网络广播音柱音频编码配置

参数填写完成后,重启下音柱

3、实现代码

internal static class ClassSIP
{
private const string FsIp = “192.168.5.250”;//这个是站长FreeSwitch服务器的地址
private const int FsEslPort = 8821;//FreeSwitch服务器默认端口
private const string FsPassword = “ClueCon”;//FreeSwitch服务器默认的一个登录密码,站长没有改

private static Socket _eslSocket;
private static readonly ConcurrentDictionary<string, string> _extCallUuid = new ConcurrentDictionary<string, string>();
private static readonly ConcurrentDictionary<string, string> _jobMap = new ConcurrentDictionary<string, string>();
private static readonly object _lockInit = new object();
private static Thread _eventThread;
private static bool _runEvent = false;
private static readonly Encoding ASCII = Encoding.ASCII;

// 事件、命令应答缓冲区完全隔离,杜绝粘包污染
private static readonly StringBuilder _eventBuf = new StringBuilder();
private static readonly object _lockEventBuf = new object();

public static bool FunInitSip()
{
lock (_lockInit)
{
if (_eslSocket != null && _eslSocket.Connected)
return true;

Cleanup();
Thread.Sleep(60);

try
{
_eslSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
SendTimeout = 3000,
ReceiveTimeout = 1000
};
//ClassSystem.FunCreateRunLog($”FreeSwitch:准备连接 {FsIp}:{FsEslPort}”);
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(FsIp), FsEslPort);
var ar = _eslSocket.BeginConnect(ep, null, null);
if (!ar.AsyncWaitHandle.WaitOne(3000))
{
_eslSocket.Close();
_eslSocket = null;
//ClassSystem.FunCreateRunLog(“FreeSwitch:连接超时”);
return false;
}
_eslSocket.EndConnect(ar);
//ClassSystem.FunCreateRunLog(“FreeSwitch:TCP连接建立成功”);

ReadCmdReply();
SendRaw($”auth {FsPassword}\r\n\r\n”);
string authRes = ReadCmdReply();
if (!authRes.Contains(“+OK”))
{
//ClassSystem.FunCreateRunLog(“FreeSwitch:认证应答异常”);
Cleanup();
return false;
}

SendRaw(“event plain BACKGROUND_JOB CHANNEL_CREATE CHANNEL_HANGUP_COMPLETE\r\n\r\n”);
ReadCmdReply();

_runEvent = true;
_eventThread = new Thread(EventWorker) { IsBackground = true };
_eventThread.Start();
//ClassSystem.FunCreateRunLog(“FreeSwitch:ESL连接FreeSWITCH成功,事件订阅完成”);
return true;
}
catch (Exception ex)
{
//ClassSystem.FunCreateRunLog($”FreeSwitch:连接异常 {ex.Message}”);
Cleanup();
return false;
}
}
}

public static bool CallSingleSpeaker(string ext, string audioPath)
{
if (_eslSocket == null || !_eslSocket.Connected)
{
//ClassSystem.FunCreateRunLog(“FreeSwitch:连接失效,尝试重连”);
if (!FunInitSip())
{
//ClassSystem.FunCreateRunLog(“FreeSwitch:重连失败”);
return false;
}
}

if (_extCallUuid.ContainsKey(ext))
{
//ClassSystem.FunCreateRunLog($”音柱 {ext} 正在播放,禁止重复呼叫”);
return false;
}

try
{
string path = audioPath.Replace(‘\\’, ‘/’);
string jobId = Guid.NewGuid().ToString(“N”);
//PCMU 适用于简单的单向通知。例如:呼叫分机,接通后播放一段欢迎语或通知,播放完毕后呼叫通常会自动挂断。
string cmd = $”bgapi originate {{origination_uuid={jobId},codec=PCMU}}user/{ext} &playback({path})\r\n\r\n”;
//适用于需要双向交互或复杂路由的场景。因为 bridge 建立了双向媒体通道,如果 playback 之后还有其他应用(例如 &record 录音,或者 &park 保持通话),或者需要监听通话过程中的 DTMF 按键,使用 bridge 会更加灵活。此外,bridge 可以桥接真实的 SIP 用户、会议(conference)或外部服务。
// string cmd = $”bgapi originate {{origination_uuid={jobId},absolute_codec_string=PCMU}}user/{ext} bridge(playback({path}))\r\n\r\n”;

SendRaw(cmd);

string res = ReadCmdReply();
// 不再打印res原始内容,彻底屏蔽大包刷屏
string jobUuid = GetHeader(res, “Job-UUID”);
if (string.IsNullOrEmpty(jobUuid))
{
//ClassSystem.FunCreateRunLog($”呼叫{ext}失败:未获取Job-UUID”);
return false;
}

_jobMap.TryAdd(jobUuid, ext);
//ClassSystem.FunCreateRunLog($”已发起音柱{ext}播放,等待分配通话ID,JobID:{jobUuid}”);
return true;
}
catch
{
return false;
}
}

public static void HangUpSingle(string ext)
{
if (_extCallUuid.TryRemove(ext, out string uuid))
{
SendRaw($”api uuid_kill {uuid}\r\n\r\n”);
ReadCmdReply();
//ClassSystem.FunCreateRunLog($”挂断音柱 {ext},通话ID:{uuid}”);
}
}

public static void HangUpAll()
{
foreach (var kv in _extCallUuid)
HangUpSingle(kv.Key);
}

public static bool IsSpeakerBusy(string ext) => _extCallUuid.ContainsKey(ext);

public static void DisposeSip()
{
lock (_lockInit)
{
_runEvent = false;
HangUpAll();
_eslSocket?.Close();
_eslSocket?.Dispose();
_eslSocket = null;
lock (_lockEventBuf) _eventBuf.Clear();
_extCallUuid.Clear();
_jobMap.Clear();
//ClassSystem.FunCreateRunLog(“ESL资源全部释放”);
}
}

#region 底层收发
private static void SendRaw(string msg)
{
if (_eslSocket == null || !_eslSocket.Connected) return;
try
{
byte[] data = ASCII.GetBytes(msg);
_eslSocket.Send(data);
}
catch (Exception ex)
{
//ClassSystem.FunCreateRunLog($”ESL发送异常:{ex.Message}”);
}
}

/// <summary>命令应答独立接收,不受事件干扰</summary>
private static string ReadCmdReply(int timeout = 3000)
{
StringBuilder buf = new StringBuilder();
byte[] temp = new byte[1024];
DateTime expire = DateTime.Now.AddMilliseconds(timeout);

while (DateTime.Now < expire)
{
if (_eslSocket == null || !_eslSocket.Connected) break;
int len;
try
{
len = _eslSocket.Receive(temp, SocketFlags.Peek);
if (len <= 0)
{
Thread.Sleep(10);
continue;
}
len = _eslSocket.Receive(temp);
}
catch (SocketException)
{
break;
}
buf.Append(ASCII.GetString(temp, 0, len));
string all = buf.ToString();
int endPos = all.IndexOf(“\r\n\r\n”);
if (endPos >= 0)
{
return all.Substring(0, endPos + 4);
}
}
return buf.ToString();
}

private static void EventWorker()
{
byte[] tempBuf = new byte[4096];
while (_runEvent)
{
if (_eslSocket == null || !_eslSocket.Connected)
{
//ClassSystem.FunCreateRunLog(“ESL链路断开,事件线程退出”);
break;
}
try
{
int len = _eslSocket.Receive(tempBuf);
if (len <= 0)
{
Thread.Sleep(20);
continue;
}
lock (_lockEventBuf)
{
_eventBuf.Append(ASCII.GetString(tempBuf, 0, len));
}

while (true)
{
string total;
lock (_lockEventBuf) total = _eventBuf.ToString();
int headerEnd = total.IndexOf(“\r\n\r\n”);
if (headerEnd < 0) break;

int contentLen = 0;
int clIndex = total.IndexOf(“Content-Length: “);
if (clIndex > -1)
{
int lineEnd = total.IndexOf(“\r\n”, clIndex);
if (lineEnd > clIndex)
{
string numStr = total.Substring(clIndex + 16, lineEnd – clIndex – 16).Trim();
int.TryParse(numStr, out contentLen);
}
}
int fullSize = headerEnd + 4 + contentLen;
if (total.Length < fullSize) break;

string oneEvent;
lock (_lockEventBuf)
{
oneEvent = _eventBuf.ToString(0, fullSize);
_eventBuf.Remove(0, fullSize);
}
// 只内部解析处理事件,【不再打印事件原文日志】
ProcessEvent(oneEvent);
}
}
catch (SocketException)
{
lock (_lockEventBuf) _eventBuf.Clear();
Thread.Sleep(80);
}
catch (Exception ex)
{
//ClassSystem.FunCreateRunLog($”事件线程异常:{ex.Message}”);
Thread.Sleep(200);
}
}
//ClassSystem.FunCreateRunLog(“ESL事件监听线程终止”);
}

private static void ProcessEvent(string evt)
{
string evtName = GetHeader(evt, “Event-Name”);

if (evtName == “BACKGROUND_JOB”)
{
string jobUuid = GetHeader(evt, “Job-UUID”);
string callUuid = GetHeader(evt, “Unique-ID”);

if (string.IsNullOrEmpty(jobUuid) || string.IsNullOrEmpty(callUuid))
return;

string ext = null;
if (!_jobMap.TryRemove(jobUuid, out ext))
{
Thread.Sleep(15);
if (!_jobMap.TryRemove(jobUuid, out ext))
{
Thread.Sleep(15);
if (!_jobMap.TryRemove(jobUuid, out ext))
{
//ClassSystem.FunCreateRunLog($”警告:BACKGROUND_JOB[{jobUuid}] 任务不存在,事件超前或已处理”);
return;
}
}
}

if (_extCallUuid.TryAdd(ext, callUuid))
{
//ClassSystem.FunCreateRunLog($”音柱{ext}成功分配通话ID:{callUuid}(BACKGROUND_JOB匹配)”);
}
else
{
//ClassSystem.FunCreateRunLog($”音柱{ext}通话ID存入失败,状态已存在”);
}
return;
}

if (evtName == “CHANNEL_HANGUP_COMPLETE”)
{
string hangUuid = GetHeader(evt, “Unique-ID”);
foreach (var kv in _extCallUuid)
{
if (kv.Value == hangUuid)
{
_extCallUuid.TryRemove(kv.Key, out _);
//ClassSystem.FunCreateRunLog($”音柱{kv.Key}通话挂断,释放ID:{hangUuid}”);
break;
}
}
return;
}
}

private static string GetHeader(string src, string key)
{
string search = $”{key}: “;
int pos = src.IndexOf(search);
if (pos < 0) return string.Empty;
int lineEnd = src.IndexOf(“\r\n”, pos);
if (lineEnd < 0) lineEnd = src.Length;
return src.Substring(pos + search.Length, lineEnd – pos – search.Length).Trim();
}

private static void Cleanup()
{
_runEvent = false;
_eslSocket?.Close();
_eslSocket?.Dispose();
_eslSocket = null;
lock (_lockEventBuf) _eventBuf.Clear();
}
#endregion
}

4、调用方式

应用程序启动事件里,调用: ClassSIP.FunInitSip();

需要播放的代码调用:  ClassSIP.CallSingleSpeaker(valueSipID, valueAudioFilePath))

//valueSipID为SIP帐号,valueAudioFilePath为要播放的音频文件

5、音频文件转换

文字转语音   https://ttsmaker.cn/

音频文件转换  https://g711.org/            PCMU第一个   PCMA第二个

搭配普通摄像头可轻松识别车牌的车牌识别模型

详细内容如有需要,请添加站长微信(内容末尾)

发送设备型号获取(注:需付费20元)

—————————————————————————————————-

网上搜集的大神训练的车牌识别模型,搭配普通摄像头可轻松识别车牌,速度快,适合那种一进一出的场景,多台车同时进出没有测试过

两个模型,一个车牌定位,一个车牌识别

站长用的是C#+OpenCvSharp+模型,用于称重过磅场景。

车牌训练时字符错乱,未按常规顺序排列,经过测试校验,已按顺序排好

字曲识别顺序

‘京’,’沪’,’津’,’渝’,’冀’,’晋’,’蒙’,’辽’,’吉’,’黑’,
‘苏’,’浙’,’皖’,’闽’,’赣’,’鲁’,’豫’,’鄂’,’湘’,’粤’,
‘桂’,’琼’,’川’,’贵’,’云’,’藏’,’陕’,’甘’,’青’,’宁’,’新’,
‘港’………………………………………………

这是识别效果

车牌识别效果
车牌识别效果

 

 

—————————————————————————————————-

详细内容如有需要,请添加站长微信,发送设备型号获取(注:需付费20元)

学海无涯 走一路学一路 站长微信I

海康威视服务器配置BMC 管理网口IP地址操作指导

步骤1 开机按delete键进入服务器管理界面选择BMC网络设置
BIOS 服务器管理界面

步骤2 进入BMC网络设置页面,设置独占 lan 参数。
BMC 网络设置页面

步骤3 选择IP 获取方式。
选择IP 获取方式

步骤4根据现场实际情况手动配置静态ip。配置静态IP

步骤5 设置完成后保存退出。 设置完成

保存退出

电脑端配置和BMC同网段下的ip,打开谷歌浏览器,在地址栏中输入步骤2中设置的BMC的ip地址,即可进入服务器系统管理平台,在username和password后面均输入admin,单击登录按钮,即可进入主界面默认用户名admin,默认密码admin

相关默认用户名和密码,请参阅:

海康威视服务器管理口地址(BMC)和管理口默认用户密码汇总

宇泰D9201 系列LTE DTU 数据传输模块接口定义说明

1 —————PWR DC 电源输入的正极
2————— GND DC 电源输入的负极
3————— A RS485 信号输入
4————— B RS485 信号输入
5 —————RX1 串口 1 数据接收
6 —————TX1 串口 1 数据发送
7—————GND 系统信号接地
8 —————RX2 串口 2 数据接收
9 —————TX2 串口 2 数据发
10————— GND 系统信号接地
11————— IO1 系统 GPIO ,最大接受 3.3V 输入、输出。非隔离
12————— IO2 系统 GPIO ,最大接受 3.3V 输入、输出。非隔离

—————————————————————————————————-

配置工具如有需要,请添加站长微信,发送设备型号获取(注:需付费0.5元)

学海无涯 走一路学一路 站长微信

宇泰UT-2102-BF参数配置说明

如果主站和从站的默认参数符合:波特率 9600bps,8 数据位,1 停止位,无校验,通信超时时间500ms。 则不用查阅下述内容,直接按照接口定义接线使用即可。

硬件准备如下:
1、隔离缓存集线器(即本产品);
2、9-24V 直流电源一个,0.5A 以上均可;
3、USB 转 RS485 转换器 1 个;
4、导线若干;
5、电脑一台(64 位操作系统);

硬件连接
如图所示将硬件连接起来

硬件连接

注意,USB 转 485 口必须接到 485 集线器的 CH1 口上。

—————————————————————————————————-

参数配置
打开配套的配置工具软件。【请添加站长微信(内容末尾),需付费0.5元)

1、 在电脑上打开软件如图

打开软件

2、右键“此电脑”点管理,然后点开“设备管理器”再点开端口查看串口号,如图

查看串口号

3、在配置软件上选择对应的串口号,如果设备管理器上有多个串口可以插拔一下 USB 看是哪个,然后点击“打开串口”。

4、按下 RS485 集线器“设置”键3 秒待 CH1 通道的指示灯闪烁松开,此时集线器进入配置模式。在软件的设备信息里点击“搜索设备”按钮,获取到设备信息说明连接成功。

集线器进入配置模式

5、参数设置,将集线器的波特率、数据位、停止位、校验位设置成与主从站设备参数一致。超时时间如果不清楚默认即可。

参数设置完成后点击“应用”然后再点击“退出设置”,查看 485 集线器 CH1 的指示灯从闪烁变为常灭则参数配置完成。CH1 和 CH2 接主站的 485 口,CH3 接从站的 485 口,就可以正常使用了。

—————————————————————————————————-
关于 “设置”键
此按键用于进入、退出参数配置模式或恢复出厂默认参数:

1、按下设置按键 3 秒待 CH1 通道绿色指示灯闪烁松开进入参数配置模式,CH1 通信口连接计算机,

通过软件可对其参数进行设置,此模式下可通过软件退出或者再按下设置按键 3 秒待 CH1 通道指示灯变为常灭松开,即退出参数配置模式。

2、按下设置按键 10 秒(3 秒后 CH1 通道指示灯会闪烁此时继续短接)待 3 个通道的指示灯常亮 1秒后熄灭恢复默认参数。默认参数为波特率 9600,8 数据位,1 停止位,无校验,通信超时时间 500MS。

—————————————————————————————————-

详细内容如有需要,请添加站长微信,发送设备型号获取(注:需付费0.5元)

学海无涯 走一路学一路 站长微信

海康威视海康服务器【hik用户密码】重置指导

hik用户(一般/普通用户):根据用户所在的组的权限指定进行某些操作。hik用户无法涉及核心操作,只能进行常规维护操作。

如忘记hik用户密码,登录root用户。(如果root用户无法远程登录则通过接显示器鼠标现场操作)。

服务器接显示器和键盘,开机即进入登录界面,在这个界面输入root回车,再次输入密码,出厂默认密码为123456,首次开机即强制要求修改新密码,修改后的新密码需要自行记住。

登陆root用户
以hik用户为例,执行passwd hik

以hik用户为例

海康威视海康服务器【root用户密码】重置指导

一、CentOS7 root忘记密码重置

1、重启操作系统待出现以下界面时,按方向键选择第一行,按e进行编辑

按e进行编辑

2、按方向键移动光标至linux16开头的行,按要求ro修改为rw,光标移动至UTF-8末尾,增加init=/bin/sh,完成后按Ctrl+x启动。

要求ro修改为rw

3、进入单用户后执行passwd修改root密码,要求设置强密码,完成后执行exec /sbin/init回到正常模式下,用新密码重新登录。

进入单用户后执行passwd修改root密码

如果出现以下乱码

出现以下乱码

请执行修改语言的为英文

export LANG=en_US

完成后执行exec /sbin/init回到多用户模式,使用新密码重新登录。

二、Centos6 root忘记密码重置:

1、出现以下界面时按任意键进入菜单

按任意键进入菜单

2、按e进行编辑

按e进行编辑

3、修改kernel开头的行

修改kernel开头的行

4、启动到单用户

启动到单用户

5、修改root密码,重启主机

修改root密码,重启主机

浪潮服务器NF8460M4S、NF8465M4S机器内存安装顺序异常导致机箱风扇转速过高

故障现象
机器风扇转速过高,达到7000多转,甚至更高。

故障原因
问题定位是因为内存没有按照正确的槽位顺序安装,DIMM JC温度无法读取,BMC认为温度无法获取而做的异常处理(把风扇转速调高),此现象并非硬件故障.

应对策略
将内存按照正常的次序调整槽位即可。
附:机型主板示意图和内存槽位顺序说明:
主板示意图:

主板示意图

2、内存槽位安装顺序说明
1)安装内存前,必须先确定内存通道被支持的对应处理器已安装。且同一台机器只能使用相同型号的内存。
2)每个CPU支持的内存槽位顺序:

CPU_0支持内存安装顺序:P0_DIMM1A/P0_DIMM2A/P0_DIMM3A/P0_DIMM4A/P0_DIMM1B/P0_DIMM2B/P0_DIMM3B/P0_DIMM4B

CPU_1支持内存安装顺序:P1_DIMM1A/P1_DIMM2AP1_DIMM3A/P1_DIMM4A/P1_DIMM1B/P1_DIMM2B/P1_DIMM3B/P1_DIMM4B

CPU_2支持内存安装顺序:P2_DIMM1A/P2_DIMM2A/P2_DIMM3A/P2_DIMM4A/P2_DIMM1B/P2_DIMM2B/P2_DIMM3B/P2_DIMM4B

CPU_3支持内存安装顺序:P3_DIMM1A/P3_DIMM2A/P3_DIMM3A/P3_DIMM4A/P3_DIMM1B/P3_DIMM2B/P3_DIMM3B/P3_DIMM4B

3)具体的内存安装组合原则如下:
A、如果系统只配置1个处理器(即CPU0),则可按P0_DIMM1A/P0_DIMM2A/P0_DIMM3A/P0_DIMM4A/P0_DIMM1B/P0_DIMM2B/P0_DIMM3B/P0_DIMM4B的顺序安装1~8条内存。
B、如果系统配置多个处理器,则优先按照平均分布安装原则安装到每个处理器支持的插槽中,剩余的内存再安装到其他
C、如果系统配置多个处理器,且内存数量为奇数,则在优先按照平均分布安装原则安装到每个处理器支持的插槽中后,将剩余的内存平均分配到其他CPU所支持的内存插槽中。

内存插槽

浪潮服务器HBA卡BIOS中查找WWN号码操作指导

当配置存储映射时,有时候无法直接搜索到HBA(sas HBA)卡的WWN号,这个时候需要手动添加。

首先需要进入HBA卡的 bios setup中查找WWN号。下面就将针对不同厂商的HBA卡分别说明如何查找。

1、LSI SAS HBA
1)开机自检的时候,按ctrl+c进入该卡的配置界面

2)进入LSI配置工具中,在SAS1064E上回车,打开卡的BIOS

3)现在能看到卡的BIOS信息了。找到SAS Address对应的号即为WWN号

2、Emulex FC HBA
1)硬件自检的时候,按CTRL+E或者ALT+E进去HBA卡的配置界面

2)进去之后能看到下面的初始配置界面,在这个界面能看到所有检测到的HBA端口

3)选择一个端口回车进入端口菜单,port name对应的即为HBA卡的WWN号

3、QLogic FC HBA
1)硬件自检的时候按CTRL+Q进入QLogic HBA BIOS(Fast!UTIL)

2)进入以后,配置界面会列举所有找到的卡,选择其中一个回车进入端口菜单

3)进入端口菜单,通常主菜单会有Adapter Settings,Selectable Boot Settings,Fiber Disk Utility,Select Host Adapter等选项,为了查看WWN号,选择Adapter Setting回车

4)在子菜单中选择Adapter Settings

5)接下来会看到HBA卡的相关设置,其中Adapter Port Name对应的即为HBA卡的WWN号

6)如果机器上安装了多个HBA卡或者双端口HBA卡,为了查看其它端口的WWN号,按ESC

直到返回QLogic Adapter Menu(步骤3),选择Select Host Adapter回车,回到步骤2的界面,

浪潮服务器UEFI模式下RAID阵列配置指导汇总

9230 M.2卡UEFI模式配置步骤(PDF文档,微信获取

9560 RAID卡在两路机器UEFI模式下配置步骤(PDF文档,微信获取

PM8060 UEFI模式RAID配置步骤(PDF文档,微信获取

3108 9361 9460等卡在八路机器UEFI模式下配置步骤(PDF文档,微信获取

3108 9361 9460等卡在四路机器UEFI模式下配置步骤(PDF文档,微信获取

3108 9361 9460等卡在两路机器UEFI模式下配置步骤(PDF文档,微信获取

PM8204在四路机器UEFI模式下配置步骤(PDF文档,微信获取

PM8204在两路机器UEFI模式下配置步骤(PDF文档,微信获取

PM8222 UEFI模式RAID配置步骤(PDF文档,微信获取

3008IR UEFI模式下配置步骤(PDF文档,微信获取

板载 RAID UEFI模式下配置步骤(PDF文档,微信获取

详细内容如有需要,请添加站长微信,发送设备型号获取(注:需付费0.5元)

学海无涯 走一路学一路 站长微信

ICP备案号:晋ICP备18007549号-1