2025-12-11 09:17:16 +08:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Net ;
using System.Net.Sockets ;
using System.Threading ;
using System.Threading.Tasks ;
using Common ;
using Dao ;
using Domain ;
using RCUHost.Protocols ;
namespace RCUHost.Implement
{
public class UpdateHostReceiver : GenericReceiverBase , IUpdateHostReceiver
{
private static log4net . ILog logger = log4net . LogManager . GetLogger ( typeof ( UpdateHostReceiver ) ) ;
private readonly int TFTP_PORT = Convert . ToInt16 ( System . Configuration . ConfigurationManager . AppSettings [ "TFTPPort" ] ) ; //TFTP通信端口
private const int BLOCK_SIZE = 512 ; //TFTP块大小512字节
public IHostRepository HostRepository { get ; set ; }
public IHostUpdateStatusRepository HostUpdateStatusRepository { get ; set ; }
private IList < UpdateHostWorker > updateHostList = new List < UpdateHostWorker > ( ) ;
/// <summary>
/// 升级
/// </summary>
/// <param name="hostUpdate"></param>
/// <param name="fileType"></param>
/// <param name="fileHref"></param>
/// <param name="fileMd5"></param>
/// <param name="hosts"></param>
public void Update ( HostUpdate hostUpdate , FileType fileType , string fileHref , string fileMd5 , IList < Host > hosts )
{
if ( hosts = = null | | hosts . Count = = 0 )
{
throw new ApplicationException ( "升级失败,没有找到需要升级的主机。" ) ;
}
if ( String . IsNullOrEmpty ( fileHref ) )
{
throw new ApplicationException ( "升级失败,无效的升级文件。" ) ;
}
string updateFile = Tools . GetApplicationPath ( ) + fileHref ;
if ( ! File . Exists ( updateFile ) )
{
throw new ApplicationException ( "升级失败,【" + updateFile + "】文件不存在。" ) ;
}
//添加升级功能标志,告诉缓存,不能被拦截
foreach ( var item in hosts )
{
2026-01-23 09:01:50 +08:00
string Key = "Upgrade_UpdateSQL_" + item . HostNumber ;
MemoryCacheHelper . Set ( Key , item . ID , DateTimeOffset . Now . AddMinutes ( 3 ) ) ;
2025-12-11 09:17:16 +08:00
}
FileInfo fileInfo = new FileInfo ( updateFile ) ;
ushort blockNum = ( ushort ) Math . Ceiling ( ( double ) fileInfo . Length / BLOCK_SIZE ) ;
this . updateHostList . Clear ( ) ;
foreach ( var host in hosts )
{
SendUpdateRequest ( host , fileMd5 , blockNum , ( byte ) fileType , fileHref . Substring ( fileHref . IndexOf ( "/" ) ) ) ; //发送升级通知
this . updateHostList . Add ( new UpdateHostWorker ( host , hostUpdate , MessageIP , fileHref , updateFile ) ) ;
if ( hostUpdate = = null ) //从api获取升级文件方式为null, 保存主机表升级状态信息
{
HostRepository . SetUpgradeStatus ( host , 0 , true ) ; //重置升级状态
}
else
{
HostUpdateStatus hostUpdateStatus = HostUpdateStatusRepository . Get ( host , hostUpdate ) ;
if ( hostUpdateStatus = = null )
{
hostUpdateStatus = new HostUpdateStatus ( ) ;
hostUpdateStatus . Host = host ;
hostUpdateStatus . HostUpdate = hostUpdate ;
hostUpdateStatus . PublishTime = DateTime . Now ;
}
hostUpdateStatus . Status = 0 ;
hostUpdateStatus . UpdatedTime = DateTime . Now ;
HostUpdateStatusRepository . SaveOrUpdate ( hostUpdateStatus ) ;
}
//logger.Error(string.Format("酒店{0}客房{1}开始升级:{2}", host.SysHotel.Code, host.RoomNumber, fileHref));
}
}
public override void Process ( ReceiverContext context1 )
{
int startIndex = StructConverter . SizeOf ( context1 . SystemHeader ) ;
UpdateHostPacketReply ? reply1 = DecodeUpdateHostPacketReply ( context1 . Data , startIndex ) ;
2026-01-23 09:01:50 +08:00
var TTT = new Tuple < ReceiverContext , UpdateHostPacketReply ? > ( context1 , reply1 ) ;
2025-12-11 09:17:16 +08:00
//logger.Error(string.Format("收到tftp升级回复命令( {0}:{1}) : {2},解析结果:{3}", context.RemoteEndPoint.Address.ToString(), context.RemoteEndPoint.Port, Tools.ByteToString(context.Data), reply.HasValue));
if ( reply1 . HasValue )
{
Task . Factory . StartNew ( ( State ) = >
{
var NNN = State as Tuple < ReceiverContext , UpdateHostPacketReply ? > ;
var context = NNN . Item1 ;
var reply = NNN . Item2 ;
var updateHostWorker = this . updateHostList . FirstOrDefault ( r = > r . Host . HostNumber = = context . SystemHeader . Value . HostNumber . ToString ( ) ) ;
2026-01-23 09:01:50 +08:00
var data = Tools . ByteToString ( context1 . Data ) ;
logger . Error ( string . Format ( "主机{0}升级返回:{1}" , context . SystemHeader . Value . HostNumber . ToString ( ) , data ) ) ;
if ( updateHostWorker = = null )
{
return ;
}
try
{
SaveSystemLog ( 30 , "升级主机" , string . Format ( "收到主机({0})升级回复命令,状态:{1}" , updateHostWorker . Host . RoomNumber , reply . Value . Status ) , "收到命令" , "RCU" , context . RemoteEndPoint . Address . ToString ( ) , updateHostWorker . Host . SysHotel . ID ) ;
}
catch ( Exception )
{
}
2025-12-11 09:17:16 +08:00
if ( updateHostWorker . HostUpdate = = null )
{
BarData bbb = new BarData ( ) ;
bbb . HostID = updateHostWorker . Host . ID ;
switch ( reply . Value . Status )
{
case UpdateHostPacketReply . Ready :
HostRepository . SetUpgradeStatus ( updateHostWorker . Host , 0 ) ; //升级就绪
//updateHostWorker.Update();
bbb . Upgrade_status = "升级就绪" ;
break ;
case UpdateHostPacketReply . Completed :
Reply ( context ) ; //升级完成回复主机
HostRepository . SetUpgradeStatus ( updateHostWorker . Host , 1 ) ; //升级完成
bbb . Upgrade_status = "升级完成" ;
break ;
2026-01-23 09:01:50 +08:00
case UpdateHostPacketReply . BlockNumError :
bbb . Upgrade_status = "块错误" ;
HostRepository . SetUpgradeStatus ( updateHostWorker . Host , 2 ) ; //升级失败
break ;
case UpdateHostPacketReply . FileTypeError :
bbb . Upgrade_status = "文件错误" ;
HostRepository . SetUpgradeStatus ( updateHostWorker . Host , 2 ) ; //升级失败
break ;
case UpdateHostPacketReply . FileMD5Error :
bbb . Upgrade_status = "文件MD5校验错误" ;
HostRepository . SetUpgradeStatus ( updateHostWorker . Host , 2 ) ; //升级失败
break ;
2025-12-11 09:17:16 +08:00
default :
HostRepository . SetUpgradeStatus ( updateHostWorker . Host , 2 ) ; //升级失败
bbb . Upgrade_status = "升级失败" ;
break ;
}
UploadCurrentVersionReceiver . UP_Grade_Json ( updateHostWorker . Host , bbb ) ;
}
else
{
HostUpdateStatus hostUpdateStatus = HostUpdateStatusRepository . Get ( updateHostWorker . Host , updateHostWorker . HostUpdate ) ;
if ( hostUpdateStatus = = null )
{
hostUpdateStatus = new HostUpdateStatus ( ) ;
hostUpdateStatus . Host = updateHostWorker . Host ;
hostUpdateStatus . HostUpdate = updateHostWorker . HostUpdate ;
hostUpdateStatus . PublishTime = DateTime . Now ;
hostUpdateStatus . Status = 0 ;
}
BarData bbb = new BarData ( ) ;
bbb . HostID = updateHostWorker . Host . ID ;
switch ( reply . Value . Status )
{
case UpdateHostPacketReply . Ready :
//updateHostWorker.Update();
hostUpdateStatus . Status = 0 ; //升级就绪
bbb . Upgrade_status = "升级就绪" ;
break ;
case UpdateHostPacketReply . Completed :
bbb . Upgrade_status = "升级完成" ;
Reply ( context ) ; //升级完成回复主机
hostUpdateStatus . Status = 1 ; //升级完成
updateHostList . Remove ( updateHostWorker ) ;
break ;
2026-01-23 09:01:50 +08:00
case UpdateHostPacketReply . BlockNumError :
bbb . Upgrade_status = "块错误" ;
hostUpdateStatus . Status = 2 ; //升级失败
updateHostList . Remove ( updateHostWorker ) ;
break ;
case UpdateHostPacketReply . FileTypeError :
bbb . Upgrade_status = "文件错误" ;
hostUpdateStatus . Status = 2 ; //升级失败
updateHostList . Remove ( updateHostWorker ) ;
break ;
case UpdateHostPacketReply . FileMD5Error :
bbb . Upgrade_status = "文件MD5校验错误" ;
hostUpdateStatus . Status = 2 ; //升级失败
updateHostList . Remove ( updateHostWorker ) ;
break ;
2025-12-11 09:17:16 +08:00
default :
bbb . Upgrade_status = "升级失败" ;
hostUpdateStatus . Status = 2 ; //升级失败
updateHostList . Remove ( updateHostWorker ) ;
break ;
}
bbb . Upgrade_DateTime = DateTime . Now . ToString ( "yyyy-MM-dd HH:mm:ss" ) ;
UploadCurrentVersionReceiver . UP_Grade_Json ( updateHostWorker . Host , bbb ) ;
hostUpdateStatus . UpdatedTime = DateTime . Now ;
HostUpdateStatusRepository . SaveOrUpdate ( hostUpdateStatus ) ;
}
2026-01-23 09:01:50 +08:00
} , TTT ) ;
2025-12-11 09:17:16 +08:00
}
}
public override CommandType CommandType
{
get { return CommandType . TFTPUpdate ; }
}
#region Private Methods
/// <summary>
/// 发送升级指令到RCU主机
/// </summary>
/// <param name="host"></param>
/// <param name="updateFileMd5">升级文件MD5值</param>
private void SendUpdateRequest ( Host host , string updateFileMd5 , ushort blockNum , byte fileType , string fileName )
{
2026-01-23 09:01:50 +08:00
logger . Error ( "主机升级:" + host . SysHotel . Code + ":" + host . RoomNumber ) ;
2025-12-11 09:17:16 +08:00
byte [ ] data = CreateUpdateRequestPacket ( updateFileMd5 , blockNum , fileType , fileName ) ;
Send ( data , host . HostNumber , host . MAC ) ; // host.IP, host.Port);
}
/// <summary>
/// 创建主机升级请求数据包
/// </summary>
/// <param name="updateFileMd5">升级文件MD5值</param>
/// <param name="blockNum">块数量</param>
/// <param name="fileType">文件类型</param>
/// <param name="fileName">文件名(含酒店编码)</param>
/// <returns></returns>
private byte [ ] CreateUpdateRequestPacket ( string updateFileMd5 , ushort blockNum , byte fileType , string fileName )
{
SystemHeader systemHeader = CreateSystemHeader ( ) ;
int headerSize = StructConverter . SizeOf ( systemHeader ) ;
byte [ ] ipArr = IPAddress . Parse ( MessageIP ) . GetAddressBytes ( ) ;
byte [ ] portArr = BitConverter . GetBytes ( ( ushort ) TFTP_PORT ) ; //tftp通讯端口
uint [ ] md5Arr = Tools . MD5StringToUIntArray ( updateFileMd5 ) ;
byte [ ] bFileName = System . Text . Encoding . Default . GetBytes ( fileName ) ; //Tools.GetBytes(fileName, 20);
using ( var stream = new MemoryStream ( ) )
{
stream . Seek ( headerSize , SeekOrigin . Begin ) ;
stream . Write ( ipArr , 0 , ipArr . Length ) ;
stream . Write ( portArr , 0 , portArr . Length ) ;
stream . Write ( BitConverter . GetBytes ( md5Arr [ 0 ] ) , 0 , 4 ) ;
stream . Write ( BitConverter . GetBytes ( md5Arr [ 1 ] ) , 0 , 4 ) ;
stream . Write ( BitConverter . GetBytes ( md5Arr [ 2 ] ) , 0 , 4 ) ;
stream . Write ( BitConverter . GetBytes ( md5Arr [ 3 ] ) , 0 , 4 ) ;
stream . Write ( BitConverter . GetBytes ( blockNum ) , 0 , 2 ) ;
stream . Write ( new byte [ ] { fileType } , 0 , 1 ) ; //文件类型, 0是bin, 1是cfg, 2是bat
stream . Write ( bFileName , 0 , bFileName . Length ) ; //文件名
stream . Write ( new byte [ ] { 0 , 0 } , 0 , 2 ) ; //补两个长度
//填充 SystemHeader
stream . Seek ( 0 , SeekOrigin . Begin ) ;
systemHeader . FrameLength = ( ushort ) stream . Length ;
systemHeader . FrameNo = 0xFFFF ;
byte [ ] headerData = StructConverter . StructToBytes ( systemHeader ) ;
stream . Write ( headerData , 0 , headerData . Length ) ;
return stream . ToArray ( ) ;
}
}
/// <summary>
/// 解码 UpdateHostPacketReply
/// </summary>
/// <param name="data"></param>
/// <param name="startIndex"></param>
/// <returns></returns>
private UpdateHostPacketReply ? DecodeUpdateHostPacketReply ( byte [ ] data , int startIndex )
{
return StructConverter . BytesToStruct ( data , startIndex , typeof ( UpdateHostPacketReply ) ) as UpdateHostPacketReply ? ;
}
#endregion
}
public class UpdateHostWorker
{
private static log4net . ILog logger = log4net . LogManager . GetLogger ( typeof ( UpdateHostWorker ) ) ;
private readonly int TFTP_PORT = Convert . ToInt16 ( System . Configuration . ConfigurationManager . AppSettings [ "TFTPPort" ] ) ; //TFTP通信端口
//private Comzept.Genesis.NetworkTools.TFTPClient tftpClient;
public Host Host { get ; private set ; }
public int Progress { get ; private set ; }
public HostUpdate HostUpdate ;
//private Task task;
public string RemoteFile ;
public string LocalFile ;
public UpdateHostWorker ( Host host , HostUpdate hostUpdate , string tftpServer , string remoteFile , string localFile )
{
this . Host = host ;
this . HostUpdate = hostUpdate ;
this . RemoteFile = remoteFile ;
this . LocalFile = localFile ;
//this.tftpClient = new Comzept.Genesis.NetworkTools.TFTPClient(tftpServer, TFTP_PORT);
//this.tftpClient.ReportCompletedProgress += new Comzept.Genesis.NetworkTools.TFTPClient.ReportCompletedProgressEventDelegate(tftpClient_ReportCompletedProgress);
}
/// <summary>
/// Puts the specified remote file.
/// </summary>
public void Update ( )
{
logger . Error ( string . Format ( "酒店({0})客房({1})主机升级: RemoteFile:{2},LocalFile:{3}" , this . Host . SysHotel . Code , this . Host . RoomNumber , this . RemoteFile , this . LocalFile ) ) ;
//this.tftpClient.Put(this.RemoteFile, this.LocalFile);
}
private void tftpClient_ReportCompletedProgress ( string remoteIP , int completedBlock , int totalBlock )
{
this . Progress = completedBlock ;
logger . Error ( string . Format ( "收到酒店({0})客房({1})主机升级总块数({2})" , this . Host . SysHotel . Code , this . Host . RoomNumber , totalBlock ) ) ;
}
public void Dispose ( )
{
//if (task != null)
//{
// task.Dispose();
//}
}
}
}