SmartOS/TinyNet/TinyClient.cpp

474 lines
9.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "TinyClient.h"
#include "JoinMessage.h"
TinyClient* TinyClient::Current = NULL;
static bool OnClientReceived(Message& msg, void* param);
static void TinyClientTask(void* param);
/******************************** 初始化和开关 ********************************/
TinyClient::TinyClient(TinyController* control)
{
assert_ptr(control);
Control = control;
Opened = false;
Joining = false;
Server = 0;
Type = Sys.Code;
LastActive = 0;
Received = NULL;
Param = NULL;
Cfg = NULL;
_TaskID = 0;
NextReport = 0;
}
void TinyClient::Open()
{
if(Opened) return;
Control->Received = OnClientReceived;
Control->Param = this;
TranID = (int)Sys.Ms();
_TaskID = Sys.AddTask(TinyClientTask, this, 0, 5000, "客户端服务");
if(Cfg->Address > 0 && Cfg->Server > 0)
{
Control->Address = Cfg->Address;
Server = Cfg->Server;
Password.Load(Cfg->Password, ArrayLength(Cfg->Password));
}
Control->Mode = 0; // 客户端只接收自己的消息
Control->Open();
Opened = true;
}
void TinyClient::Close()
{
if(!Opened) return;
Sys.RemoveTask(_TaskID);
Control->Received = NULL;
Control->Param = NULL;
Control->Close();
Opened = false;
}
/******************************** 收发中心 ********************************/
bool TinyClient::Send(TinyMessage& msg)
{
assert_param2(this, "令牌客户端未初始化");
assert_param2(Control, "令牌控制器未初始化");
// 未组网时,禁止发其它消息。组网消息通过广播发出,不经过这里
if(!Server) return false;
// 设置网关地址
if(!msg.Dest) msg.Dest = Server;
return Control->Send(msg);
}
bool TinyClient::Reply(TinyMessage& msg)
{
assert_param2(this, "令牌客户端未初始化");
assert_param2(Control, "令牌控制器未初始化");
// 未组网时,禁止发其它消息。组网消息通过广播发出,不经过这里
if(!Server) return false;
if(!msg.Dest) msg.Dest = Server;
return Control->Reply(msg);
}
bool OnClientReceived(Message& msg, void* param)
{
TinyClient* client = (TinyClient*)param;
assert_ptr(client);
client->OnReceive((TinyMessage&)msg);
return true;
}
bool TinyClient::OnReceive(TinyMessage& msg)
{
// 不处理来自网关以外的消息
//if(Server == 0 || Server != msg.Dest) return true;
//debug_printf("源地址: 0x%08X 网关地址:0x%08X \r\n",Server, msg.Src);
if(Type != 0x01C8)
if(msg.Code != 0x01&& Server != msg.Src) return true;//不是无线中继,不是组网消息。不是被组网网关消息,不受其它消息设备控制.
if(msg.Src == Server) LastActive = Sys.Ms();
switch(msg.Code)
{
case 0x01:
OnJoin(msg);
break;
case 0x02:
OnDisjoin(msg);
break;
case 0x03:
OnPing(msg);
break;
case 0x05:
case 0x15:
OnRead(msg);
break;
case 0x06:
case 0x16:
OnWrite(msg);
break;
}
// 消息转发
if(Received) return Received(msg, Param);
return true;
}
/******************************** 数据区 ********************************/
/*
请求1起始 + 1大小
响应1起始 + N数据
错误错误码2 + 1起始 + 1大小
*/
void TinyClient::OnRead(const TinyMessage& msg)
{
if(msg.Reply) return;
if(msg.Length < 2) return;
// 起始地址为7位压缩编码整数
Stream ms = msg.ToStream();
uint offset = ms.ReadEncodeInt();
uint len = ms.ReadEncodeInt();
// 准备响应数据
TinyMessage rs;
rs.Code = msg.Code;
Stream ms2 = rs.ToStream();
ByteArray& bs = Store.Data;
int remain = bs.Length() - offset;
if(remain < 0)
{
rs.Error = true;
ms2.Write((byte)2);
ms2.WriteEncodeInt(offset);
ms2.WriteEncodeInt(len);
}
else
{
ms2.WriteEncodeInt(offset);
if(len > remain) len = remain;
if(len > 0) ms2.Write(bs.GetBuffer(), offset, len);
}
rs.Length = ms2.Position();
Reply(rs);
Report(rs);//接受写入一次,刷新服务端
}
/*
请求1起始 + N数据
响应1起始 + 1大小
错误错误码2 + 1起始 + 1大小
*/
void TinyClient::OnWrite(const TinyMessage& msg)
{
if(msg.Reply) return;
if(msg.Length < 2) return;
// 起始地址为7位压缩编码整数
Stream ms = msg.ToStream();
uint offset = ms.ReadEncodeInt();
if(WriteCfg(offset,ms)) return;
// 准备响应数据
TinyMessage rs;
rs.Code = msg.Code;
Stream ms2 = rs.ToStream();
// 剩余可写字节数
uint len = ms.Remain();
int remain = Store.Data.Length() - offset;
if(remain < 0)
{
rs.Error = true;
ms2.Write((byte)2);
ms2.WriteEncodeInt(offset);
ms2.WriteEncodeInt(len);
}
else
{
ms2.WriteEncodeInt(offset);
if(len > remain) len = remain;
ByteArray bs(ms.Current(), len);
int count = Store.Write(offset, bs);
ms2.WriteEncodeInt(count);
}
rs.Length = ms2.Position();
Reply(rs);
}
bool TinyClient::WriteCfg(uint offset, Stream ms)
{
if(offset<64) return false;
ByteArray cfg(&Cfg, Cfg->Length);
uint len = ms.Remain();
ByteArray bs(ms.Current(), len);
bs.CopyTo(&cfg[offset-64],len);
Cfg->Save();
debug_printf("修改后的设备ID 0x%08X ", Cfg->Address);
return true;
}
void TinyClient::Report(Message& msg)
{
// 没有服务端时不要上报
if(!Server) return;
Stream ms = msg.ToStream();
ms.Write((byte)0x01); // 子功能码
ms.Write((byte)0x00); // 起始地址
ms.Write((byte)Store.Data.Length()); // 长度
ms.Write(Store.Data);
msg.Length = ms.Position();
}
bool TinyClient::Report(uint offset, byte dat)
{
TinyMessage msg;
msg.Code = 0x05;
Stream ms = msg.ToStream();
ms.WriteEncodeInt(offset);
ms.Write(dat);
msg.Length = ms.Position();
return Reply(msg);
}
bool TinyClient::Report(uint offset, const ByteArray& bs)
{
TinyMessage msg;
msg.Code = 0x05;
Stream ms = msg.ToStream();
ms.WriteEncodeInt(offset);
ms.Write(bs);
msg.Length = ms.Position();
return Reply(msg);
}
void TinyClient::ReportAsync(uint offset)
{
if(this == NULL) return;
if(offset >= Store.Data.Length()) return;
NextReport = offset;
// 延迟200ms上报期间有其它上报任务到来将会覆盖
Sys.SetTask(_TaskID, true, 200);
}
/******************************** 常用系统级消息 ********************************/
void TinyClientTask(void* param)
{
assert_ptr(param);
TinyClient* client = (TinyClient*)param;
uint offset = client->NextReport;
assert_param2(offset == 0 || offset < 0x10, "自动上报偏移量异常!");
if(offset)
{
// 检查索引,否则数组越界
ByteArray& bs = client->Store.Data;
if(bs.Length() > offset) client->Report(offset, bs[offset]);
client->NextReport = 0;
return;
}
if(client->Server == 0 || client->Joining) client->Join();
if(client->Server != 0) client->Ping();
}
// 发送发现消息,告诉大家我在这
// 格式2设备类型 + N系统ID
void TinyClient::Join()
{
TinyMessage msg;
msg.Code = 1;
// 发送的广播消息设备类型和系统ID
JoinMessage dm;
dm.Kind = Type;
dm.HardID = Sys.ID;
dm.TranID = TranID;
dm.WriteMessage(msg);
dm.Show(true);
Control->Broadcast(msg);
}
// 组网
bool TinyClient::OnJoin(const TinyMessage& msg)
{
// 客户端只处理Discover响应
if(!msg.Reply || msg.Error) return true;
// 解析数据
JoinMessage dm;
dm.ReadMessage(msg);
dm.Show(true);
// 校验不对
if(TranID != dm.TranID)
{
debug_printf("发现响应序列号 0x%08X 不等于内部序列号 0x%08X \r\n", dm.TranID, TranID);
//return true;
}
Joining = false;
Cfg->Address = dm.Address;
Control->Address = dm.Address;
Password = dm.Password;
Password.Save(Cfg->Password, ArrayLength(Cfg->Password));
// 记住服务端地址
Server = dm.Server;
Cfg->Server = dm.Server;
Cfg->Channel = dm.Channel;
Cfg->Speed = dm.Speed * 10;
// 服务端组网密码,退网使用
Cfg->ServerKey[0] = dm.HardID.Length();
dm.HardID.Save(Cfg->ServerKey, ArrayLength(Cfg->ServerKey));
//debug_printf("组网成功!\r\n");
debug_printf("组网成功!网关 0x%02X 分配 0x%02X ,频道:%d传输速率%dkbps密码", Server, dm.Address, dm.Channel, Cfg->Speed);
// 取消Join任务启动Ping任务
ushort time = Cfg->PingTime;
if(time < 5) time = 5;
if(time > 60) time = 60;
Cfg->PingTime = time;
Sys.SetTaskPeriod(_TaskID, time * 1000);
// 组网成功更新一次最后活跃时间
LastActive = Sys.Ms();
// 保存配置
Cfg->Save();
return true;
}
void TinyClient::DisJoin()
{
debug_printf("TinyClient::DisJoin 退网 \r\n");
TinyMessage msg;
msg.Code = 2;
// 发送的广播消息设备类型和系统ID
JoinMessage dm;
dm.Kind = Type;
dm.HardID = Sys.ID;
dm.TranID = TranID;
dm.WriteMessage(msg);
dm.Show(true);
Send(msg);
}
// 离网
bool TinyClient::OnDisjoin(const TinyMessage& msg)
{
return true;
}
// 心跳
void TinyClient::Ping()
{
ushort off = Cfg->OfflineTime;
if(off < 10) off = 10;
if(LastActive > 0 && LastActive + off * 1000 < Sys.Ms())
{
if(Server == 0) return;
debug_printf("%d 秒无法联系网关,无线网可能已经掉线,重新组网,其它任务正常处理\r\n", off);
Sys.SetTaskPeriod(_TaskID, 5000);
// 掉线以后,重发组网信息,基本功能继续执行
Joining = true;
//Server = 0;
//Password.SetLength(0);
//return;
}
TinyMessage msg;
msg.Code = 3;
// 没事的时候心跳指令承载0x01子功能码作为数据上报
Report(msg);
Send(msg);
if(LastActive == 0) LastActive = Sys.Ms();
}
bool TinyClient::OnPing(const TinyMessage& msg)
{
// 仅处理来自网关的消息
if(Server == 0 || Server != msg.Dest) return true;
// 忽略响应消息
if(msg.Reply)
{
if(msg.Src == Server) LastActive = Sys.Ms();
return true;
}
debug_printf("TinyClient::OnPing Length=%d\r\n", msg.Length);
return true;
}