<p><span style=" font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 2.1em;">模拟GPRS数据通讯 -</span><span style="font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 2.1em; color: rgb(118, 146, 60);"> <span style="color: rgb(0, 176, 80);">(第十五讲)</span></span><br/></p><p>视频讲解如下:</p><p><embed src="//player.bilibili.com/player.html?aid=475565922&bvid=BV1dK411978L&cid=905304106&page=1&danmaku=0" width="817" height="460" wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true"/></p><p><br/></p><p style="white-space: normal;">工程源码下载:<a href="/Course?id=5631974000295" target="_blank" style="white-space: normal;">GPS定位系统系列教程源码下载</a></p><p style="white-space: normal;"><br/></p><p style="white-space: normal;"><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; white-space: normal; "><span style="margin: 0px; padding: 0px; list-style: none; border: 0px; font-size: 24px;">1、代码修改</span></strong></p><p style="white-space: normal;"><span style="color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "> 到目前为止,我们整个项目已经实现了上位机与模拟器的基本通讯,接下来,我们需要完善通讯协议中的数据载体部分,将我们需要上报的GPS坐标通过模拟器上传至上位机程序。</span></p><p style="white-space: normal;"><span style="color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "> 我们的载体定义如下:</span></p><p style="white-space: normal;"><span style="color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "></span></p><table border="1" style="border-right: none; border-bottom: none; border-image: initial; border-left: 1px solid rgb(102, 102, 102); border-top: 1px solid rgb(102, 102, 102);" width="161"><tbody><tr style="height:6px" class="firstRow"><td width="84" valign="top" style="border-width: 1px; border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-style: solid; border-right-style: solid; padding: 5px;"><p style="text-align:center"><span style="font-family:宋体;font-size:14px">WORD</span></p></td><td width="77" valign="top" style="border-width: 1px; border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-style: solid; border-right-style: solid; padding: 5px;"><p><span style="font-family:宋体;font-size:14px"><span style="font-family:宋体">用户</span><span style="font-family:宋体">ID</span></span></p></td></tr><tr style="height:6px"><td width="84" valign="top" style="border-left-width: 1px; border-left-color: windowtext; border-top: none; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p style="text-align:center"><span style="font-family: 宋体;letter-spacing: 0;font-size: 14px">DWORD</span></p></td><td width="77" valign="top" style="border-left-width: 1px; border-left-color: windowtext; border-top: none; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family:新宋体;font-size:13px">经度</span></p></td></tr><tr style="height:6px"><td width="84" valign="top" style="border-left-width: 1px; border-left-color: windowtext; border-top: none; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p style="text-align:center"><span style="font-family: 宋体;letter-spacing: 0;font-size: 14px">DWORD</span></p></td><td width="77" valign="top" style="border-left-width: 1px; border-left-color: windowtext; border-top: none; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family:新宋体;font-size:13px">纬度</span></p></td></tr></tbody></table><p style="white-space: normal;"><span style="color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "></span> <br/></p><p style="white-space: normal;"> 由于是进行模拟测试,所以我们下位机是采用模拟的方式进行测试。模拟程序采用《<a href="/Course?id=4443824000016" target="_blank" title="如何实现TCP通信">如何实现TCP通信</a>》原程序进行修改,源码我会放在工程包里面,供大家参考。上位机的<span style=" color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;">主要修改的文件,以及修改内容说明如下:</span></p><p style="white-space: normal;"><span style="color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><img src="/upload/image/6379910118177447998181853.jpg" title="QQ截图20220918123246.jpg" alt="QQ截图20220918123246.jpg"/></span></p><p style="white-space: normal;"><br/></p><p>_GpsAnalysis.cs:添加对载荷数据的解析,解析出用户ID和经纬度</p><pre class="brush:c#;toolbar:false">/// <summary>
/// 接收GPS数据
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
public bool Abnormal()
{
try
{
// 接收数据处理
c.user = (int)p.context.Skip(0).Take(2).ToArray().ByteToInt(); // 当前用户ID
c.Longitude = (Int32)p.context.Skip(2).Take(4).ToArray().ByteToInt() / (decimal)100000; // 经度
c.Latitude = (Int32)p.context.Skip(6).Take(4).ToArray().ByteToInt() / (decimal)100000; // 纬度
System.Diagnostics.Debug.WriteLine($"接收到GPS数据了:{c.user} {c.Longitude},{c.Latitude}");
// 将最新的数据发送到前端
Res.SenseDataMsg?.Invoke((int)p.equipmentNo.ByteToInt(), c.user, c.Longitude, c.Latitude);
return true;
}
catch { return false; }
}</pre><p><br/></p><p>GpsModel.cs:新增用户ID和经纬度的存储变量</p><pre class="brush:c#;toolbar:false">/// <summary>
/// 用户id
/// </summary>
public int user { get; set; }
/// <summary>
/// 经度
/// </summary>
public decimal Longitude { get; set; }
/// <summary>
/// 纬度
/// </summary>
public decimal Latitude { get; set; }</pre><p><br/></p><p>TcpSocketConnection.cs:链接断开的log文字写错了,写成“端开”了。。。</p><p> </p><p>Link.Res.cs:新增数据回调的参数</p><pre class="brush:c#;toolbar:false">// 下位机消息:设备号,用户ID,经度、纬度
public static Action<int ,int, decimal, decimal> SenseDataMsg = null;</pre><p><br/></p><p>HomeController.cs:新增获取最新的坐标的API:GetMap()</p><pre class="brush:c#;toolbar:false">/// <summary>
/// 获取最新的坐标
/// </summary>
/// <returns></returns>
public ActionResult GetMap()
{
AjaxResult res = new AjaxResult() { Success = false };
try
{
User user = HttpContext.Session.GetString("user").ToObject<User>();
if (user != null)
{
foreach(var p in Res.NewMap)
{
// 只获取当前用户的坐标信息
if(p.user == user.ID)
{
res.Data = new List<decimal>() { p.Longitude, p.Latitude }; //new List<double>() { 119.246, 26.08125 };
res.Success = true;
break;
}
}
}
}
catch (Exception e)
{
res.ErrorCode = e.Message;
}
return Content(res.ToJson());
}</pre><p><br/></p><p>map.cshtml:新增post请求,向后端的GetMap(),不断获取最新的gps坐标</p><pre class="brush:js;toolbar:false"><script src="/js/jquery.min 1.11.3.js"></script>
<script>
var Longitude = 0;
var Latitude = 0;
setInterval(function(){
$.post('GetMap', null, function (resJson) {
var res = JSON.parse(resJson);
if (res.Success) {
var data = res.Data;
if (Longitude != data[0] || Latitude != data[1]) {
console.log(data);
Longitude = data[0];
Latitude = data[1];
ListLocation(data[0], data[1]);
}
}
});
}, 1000);
</script></pre><p><br/></p><p>WebApplicationGPS.Res.cs:新增最新GPS坐标的缓存:NewMap</p><pre class="brush:c#;toolbar:false">using DbEntity;
namespace WebApplicationGPS
{
public static class Res
{
/// <summary>
/// 数据库链接字符串
/// </summary>
public static string? SqlStr { set; get; }
/// <summary>
/// 数据库
/// </summary>
public static MyDbContext MyDb { set; get; }
/// <summary>
/// 最新坐标:用户ID,经度、纬度
/// </summary>
public static List<MapPack> NewMap { set; get; } = new List<MapPack>();
}
public class MapPack
{
public int user { set; get; }
public decimal Longitude { set; get; }
public decimal Latitude { set; get; }
}
}</pre><p>ServerInit.cs:修改回调</p><pre class="brush:c#;toolbar:false">/// <summary>
/// 事件初始化
/// </summary>
private void EventInit()
{
// 保存GPS数据
Link.Res.SenseDataMsg = (dev, a, b, c) =>
{
System.Diagnostics.Debug.WriteLine($"SenseDataMsg 接收到数据:{a},{b},{c}");
// 如果用户不存在,属于非法数据,可以忽略
User user = Res.MyDb.Tb_User.FirstOrDefault(x => x.ID == a);
if (user == null) return;
// 将接收到的数据更新到缓存中,供前端显示
MapPack p = Res.NewMap.FirstOrDefault(x => x.user == a);
if(p != null)
{
p.Longitude = b;
p.Latitude = c;
}
else
{
Res.NewMap.Add(new MapPack() {
user = a,
Longitude = b,
Latitude = c
});
}
// 将接收到的数据保存到数据库
using (MyDbContext db = new MyDbContext() { config = Res.SqlStr })
{
GpsData data = new GpsData()
{
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
UserId = user.UserId,
gps = $"{b},{c}"
};
db.Tb_GpsData.Add(data);
db.SaveChanges();
}
};
// 后面可以扩展
/* ... */
}</pre><p style="white-space: normal;"><br/></p><p><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><span style="margin: 0px; padding: 0px; list-style: none; border: 0px; font-size: 24px;">2、关于模拟器</span></strong></p><p> 拟程序采用《<a href="/Course?id=4443824000016" target="_blank" title="如何实现TCP通信" style="white-space: normal;">如何实现TCP通信</a>》原程序进行修改,模拟器上报的主要数据组装代码如下:</p><pre class="brush:c#;toolbar:false">/// <summary>
/// 组装数据
/// </summary>
/// <param name="oData"></param>
/// <returns></returns>
private byte[] order_11()
{
byte[] oData = new byte[500];
int ret = 0;
oData[ret++] = 0xCC; // 网络识别码
oData[ret++] = 0x01; // 设备类型
oData[ret++] = 0x00; // 设备号H
oData[ret++] = 0x01; // 设备号L
int PackNum = 0 ; // 包号应该是按累加处理的,我这里测试,直接写死
oData[ret++] = (byte)(PackNum >> 8); // 包号H
oData[ret++] = (byte)(PackNum & 0x00FF); // 包号L
oData[ret++] = 0x11; // 功能码
// 用户ID
int user = (int)numericUpDown1.Value;
oData[ret++] = (byte)(user >> 8); // 用户ID H
oData[ret++] = (byte)(user & 0x00FF); // 用户ID L
// 经度
long a = (long)((decimal)numericUpDown2.Value * 100000);
oData[ret++] = (byte)((a >> 24) & 0x000000FF);
oData[ret++] = (byte)((a >> 16) & 0x000000FF);
oData[ret++] = (byte)((a >> 8) & 0x000000FF);
oData[ret++] = (byte)(a & 0x000000FF);
// 纬度
long b = (long)((decimal)numericUpDown3.Value * 100000);
oData[ret++] = (byte)((b >> 24) & 0x000000FF);
oData[ret++] = (byte)((b >> 16) & 0x000000FF);
oData[ret++] = (byte)((b >> 8) & 0x000000FF);
oData[ret++] = (byte)(b & 0x000000FF);
// 校验
oData[ret++] = GetSum(oData);
return oData.Take(ret).ToArray();
}</pre><p> 界面如下:<br/></p><p><img src="/upload/image/6379910193250549871695970.jpg" title="QQ截图20220918124520.jpg" alt="QQ截图20220918124520.jpg"/></p><p> 这里要注意了,模拟器必须是作为客户端使用,端口为2233,这个端口、设备类型、设备号等信息在上位机LinkInit()函数中有定义:</p><p><img src="/upload/image/6379910208062976715867324.jpg" title="QQ截图20220918124751.jpg" alt="QQ截图20220918124751.jpg"/></p><p><br/></p><p><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><span style="margin: 0px; padding: 0px; list-style: none; border: 0px; font-size: 24px;">3、运行效果演示</span></strong></p><p><img src="/upload/image/6379917817283143588536123.gif" title="1 (1).gif" alt="1 (1).gif"/></p><p><br/></p><p>报表数据如下:</p><p><img src="/upload/image/6379910276273956549821440.jpg" title="QQ截图20220918125832.jpg" alt="QQ截图20220918125832.jpg"/></p>
模拟GPRS数据通讯 - (第十五讲)
视频讲解如下:
工程源码下载:GPS定位系统系列教程源码下载
1、代码修改
到目前为止,我们整个项目已经实现了上位机与模拟器的基本通讯,接下来,我们需要完善通讯协议中的数据载体部分,将我们需要上报的GPS坐标通过模拟器上传至上位机程序。
我们的载体定义如下:
由于是进行模拟测试,所以我们下位机是采用模拟的方式进行测试。模拟程序采用《如何实现TCP通信 》原程序进行修改,源码我会放在工程包里面,供大家参考。上位机的主要修改的文件,以及修改内容说明如下:
_GpsAnalysis.cs:添加对载荷数据的解析,解析出用户ID和经纬度
/// <summary>
/// 接收GPS数据
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
public bool Abnormal()
{
try
{
// 接收数据处理
c.user = (int)p.context.Skip(0).Take(2).ToArray().ByteToInt(); // 当前用户ID
c.Longitude = (Int32)p.context.Skip(2).Take(4).ToArray().ByteToInt() / (decimal)100000; // 经度
c.Latitude = (Int32)p.context.Skip(6).Take(4).ToArray().ByteToInt() / (decimal)100000; // 纬度
System.Diagnostics.Debug.WriteLine($"接收到GPS数据了:{c.user} {c.Longitude},{c.Latitude}");
// 将最新的数据发送到前端
Res.SenseDataMsg?.Invoke((int)p.equipmentNo.ByteToInt(), c.user, c.Longitude, c.Latitude);
return true;
}
catch { return false; }
}
GpsModel.cs:新增用户ID和经纬度的存储变量
/// <summary>
/// 用户id
/// </summary>
public int user { get; set; }
/// <summary>
/// 经度
/// </summary>
public decimal Longitude { get; set; }
/// <summary>
/// 纬度
/// </summary>
public decimal Latitude { get; set; }
TcpSocketConnection.cs:链接断开的log文字写错了,写成“端开”了。。。
Link.Res.cs:新增数据回调的参数
// 下位机消息:设备号,用户ID,经度、纬度
public static Action<int ,int, decimal, decimal> SenseDataMsg = null;
HomeController.cs:新增获取最新的坐标的API:GetMap()
/// <summary>
/// 获取最新的坐标
/// </summary>
/// <returns></returns>
public ActionResult GetMap()
{
AjaxResult res = new AjaxResult() { Success = false };
try
{
User user = HttpContext.Session.GetString("user").ToObject<User>();
if (user != null)
{
foreach(var p in Res.NewMap)
{
// 只获取当前用户的坐标信息
if(p.user == user.ID)
{
res.Data = new List<decimal>() { p.Longitude, p.Latitude }; //new List<double>() { 119.246, 26.08125 };
res.Success = true;
break;
}
}
}
}
catch (Exception e)
{
res.ErrorCode = e.Message;
}
return Content(res.ToJson());
}
map.cshtml:新增post请求,向后端的GetMap(),不断获取最新的gps坐标
<script src="/js/jquery.min 1.11.3.js"></script>
<script>
var Longitude = 0;
var Latitude = 0;
setInterval(function(){
$.post('GetMap', null, function (resJson) {
var res = JSON.parse(resJson);
if (res.Success) {
var data = res.Data;
if (Longitude != data[0] || Latitude != data[1]) {
console.log(data);
Longitude = data[0];
Latitude = data[1];
ListLocation(data[0], data[1]);
}
}
});
}, 1000);
</script>
WebApplicationGPS.Res.cs:新增最新GPS坐标的缓存:NewMap
using DbEntity;
namespace WebApplicationGPS
{
public static class Res
{
/// <summary>
/// 数据库链接字符串
/// </summary>
public static string? SqlStr { set; get; }
/// <summary>
/// 数据库
/// </summary>
public static MyDbContext MyDb { set; get; }
/// <summary>
/// 最新坐标:用户ID,经度、纬度
/// </summary>
public static List<MapPack> NewMap { set; get; } = new List<MapPack>();
}
public class MapPack
{
public int user { set; get; }
public decimal Longitude { set; get; }
public decimal Latitude { set; get; }
}
} ServerInit.cs:修改回调
/// <summary>
/// 事件初始化
/// </summary>
private void EventInit()
{
// 保存GPS数据
Link.Res.SenseDataMsg = (dev, a, b, c) =>
{
System.Diagnostics.Debug.WriteLine($"SenseDataMsg 接收到数据:{a},{b},{c}");
// 如果用户不存在,属于非法数据,可以忽略
User user = Res.MyDb.Tb_User.FirstOrDefault(x => x.ID == a);
if (user == null) return;
// 将接收到的数据更新到缓存中,供前端显示
MapPack p = Res.NewMap.FirstOrDefault(x => x.user == a);
if(p != null)
{
p.Longitude = b;
p.Latitude = c;
}
else
{
Res.NewMap.Add(new MapPack() {
user = a,
Longitude = b,
Latitude = c
});
}
// 将接收到的数据保存到数据库
using (MyDbContext db = new MyDbContext() { config = Res.SqlStr })
{
GpsData data = new GpsData()
{
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
UserId = user.UserId,
gps = $"{b},{c}"
};
db.Tb_GpsData.Add(data);
db.SaveChanges();
}
};
// 后面可以扩展
/* ... */
}
2、关于模拟器
拟程序采用《如何实现TCP通信 》原程序进行修改,模拟器上报的主要数据组装代码如下:
/// <summary>
/// 组装数据
/// </summary>
/// <param name="oData"></param>
/// <returns></returns>
private byte[] order_11()
{
byte[] oData = new byte[500];
int ret = 0;
oData[ret++] = 0xCC; // 网络识别码
oData[ret++] = 0x01; // 设备类型
oData[ret++] = 0x00; // 设备号H
oData[ret++] = 0x01; // 设备号L
int PackNum = 0 ; // 包号应该是按累加处理的,我这里测试,直接写死
oData[ret++] = (byte)(PackNum >> 8); // 包号H
oData[ret++] = (byte)(PackNum & 0x00FF); // 包号L
oData[ret++] = 0x11; // 功能码
// 用户ID
int user = (int)numericUpDown1.Value;
oData[ret++] = (byte)(user >> 8); // 用户ID H
oData[ret++] = (byte)(user & 0x00FF); // 用户ID L
// 经度
long a = (long)((decimal)numericUpDown2.Value * 100000);
oData[ret++] = (byte)((a >> 24) & 0x000000FF);
oData[ret++] = (byte)((a >> 16) & 0x000000FF);
oData[ret++] = (byte)((a >> 8) & 0x000000FF);
oData[ret++] = (byte)(a & 0x000000FF);
// 纬度
long b = (long)((decimal)numericUpDown3.Value * 100000);
oData[ret++] = (byte)((b >> 24) & 0x000000FF);
oData[ret++] = (byte)((b >> 16) & 0x000000FF);
oData[ret++] = (byte)((b >> 8) & 0x000000FF);
oData[ret++] = (byte)(b & 0x000000FF);
// 校验
oData[ret++] = GetSum(oData);
return oData.Take(ret).ToArray();
} 界面如下:
这里要注意了,模拟器必须是作为客户端使用,端口为2233,这个端口、设备类型、设备号等信息在上位机LinkInit()函数中有定义:
3、运行效果演示
报表数据如下: