串口通信类(2)

现假设有一系列产品我们需要使用软件控制其运作,我们可以通过向设备发送各种指令来达到此要求,首先由于产品种类较多,而且有些产品是纵向升级的,升级之后的产品基本上保持原有功能基础上加了些新功能,基于此要求,我们在设计相应类时,可以使用继承的方法,通信基类使用抽象类不允许直接实例化,仅提供一般串口操作的方法,比如发送指令及关串口等操作(初始化串口及打开串口操作在构造函数中完成),这样有任何产品只要从这个类继承即可调用基类函数,而且考虑到产品升级,只要继承相应的基类,重新相应的方法即可,即可以重用代码,结构又比较清晰,下面是一些示例代码

using  System;
using  System.IO.Ports;
using  System.Threading;

namespace  TripodDemo
{
    
/**//// <summary>
    
/// 串口操作通用类,抽象类防止类别直接实例化
    
/// </summary>

    public abstract class ComPort : IDisposable
    
{
        
/**//// <summary>
        
/// 使用变量锁定对象
        
/// </summary>

        private object objComm = new object();

        
/**//// <summary>
        
/// 串口对象
        
/// </summary>

        private SerialPort com;

        
private bool disposed = false;

        
SerialPort#region SerialPort
        
public ComPort()
        
{
            com 
= new SerialPort("COM1"9600);
            OpenCom();
        }


        
public ComPort(SerialPort sp)
        
{
            com 
= sp;
            OpenCom();
        }


        
private void OpenCom()
        
{
            
try
            
{
                
if (!com.IsOpen)
                
{
                    com.ReadTimeout 
= 3000;
                    com.WriteTimeout 
= 3000;
                    com.Open();
                }

            }

            
catch (Exception ex)
            
{
                
throw new Exception(ex.Message);
            }

        }

        
#endregion


        
Dispose & Close#region Dispose & Close
        
public void Dispose()
        
{
            Dispose(
true);
        }


        
private void Dispose(bool disposing)
        
{
            
if (!disposed && disposing && com != null && com.IsOpen)
            
{
                com.Close();
                disposed 
= true;
            }

        }


        
public void Close()
        
{
            Dispose(
true);
        }

        
#endregion


        
发送数据#region 发送数据
        
/**//// <summary>
        
/// 发送数据
        
/// </summary>
        
/// <param name="bwrite">写数据</param>
        
/// <param name="returnDataLen">读数据长度</param>
        
/// <param name="bread">读数据</param>
        
/// <param name="msel">超时毫秒</param>
        
/// <returns>是否执行成功</returns>

        public bool SendData(byte[] bwrite, int returnDataLen, out byte[] bread, int msel)
        
{
            
lock (objComm)
            
{
                bread 
= new byte[returnDataLen];
                
long l = DateTime.UtcNow.Ticks + msel * 10000;
                com.DiscardInBuffer();
                com.DiscardOutBuffer();
                com.Write(bwrite, 
0, bwrite.Length);

                
while (com.BytesToRead < returnDataLen && DateTime.UtcNow.Ticks < l)
                
{
                    Thread.Sleep(
1);
                }


                
//串口无反应
                if (com.BytesToRead < returnDataLen)
                
{
                    
return false;
                }


                
try
                
{
                    com.Read(bread, 
0, bread.Length);
                    
return true;
                }

                
catch (TimeoutException)
                
{
                    
return false;
                }

            }

        }

        
#endregion

    }

}


using  System;
using  System.IO.Ports;
using  System.Text;

namespace  TripodDemo
{
    
public class SerialQ01 : ComPort
    
{
        
构造函数#region 构造函数
        
public SerialQ01()
            : 
base()
        
{
            
//constructor
        }


        
public SerialQ01(SerialPort sp)
            : 
base(sp)
        
{
            
//constructor
        }

        
#endregion


        
读时间#region 读时间
        
public bool ReadDeviceTime(byte macno, out string time)
        
{
            
int retLen = 10;
            
byte[] bread;

            
//构建命令
            byte b1 = 0xAA;
            
byte b2 = 0x00;
            
byte b3 = 0x32;

            
byte[] bwrite = { b1, b2, b2, macno, macno, b3, b3 };
            
bool bRet = SendData(bwrite, retLen, out bread, 1000);

#if DEBUG
            
foreach (byte b in bread)
            
{
                System.Diagnostics.Debug.WriteLine(b.ToString(
"X2"));
            }

#endif

            time 
= string.Empty;
            
if (bRet)
            
{
                
if (bread[0== 0xCC && bread[1== 0x30 && bread[2== 0x30)
                
{
                    time 
= string.Format("20{0}-{1}-{2} {3}:{4}", bread[3].ToString("X2"), bread[4].ToString("X2"), bread[5].ToString("X2"), bread[6].ToString("X2"), bread[7].ToString("X2"));
                    
return true;
                }

                
else
                
{
                    
return false;
                }

            }

            
else
            
{
                
return false;
            }

        }

        
#endregion


        
设置时间#region 设置时间
        
public bool SetDeviceTime(byte macno, DateTime time)
        
{
            
int retLen = 10;
            
byte[] bread;

            
//构建命令
            byte b1 = 0xAA;
            
byte b2 = 0x00;
            
byte b3 = 0x31;

            
byte year = Convert.ToByte(time.Year.ToString().Substring(22), 16);
            
byte month = Convert.ToByte(time.Month.ToString(), 16);
            
byte day = Convert.ToByte(time.Day.ToString(), 16);
            
byte hour = Convert.ToByte(time.Hour.ToString(), 16);
            
byte min = Convert.ToByte(time.Minute.ToString(), 16);
            
byte sec = Convert.ToByte(time.Second.ToString(), 16);

            
byte xor = (byte)(year ^ month ^ day ^ hour ^ min ^ sec);

            
byte[] bwrite = { b1, b2, b2, macno, macno, b3, b3, year, month, day, hour, min, sec, xor };
            
bool bRet = SendData(bwrite, retLen, out bread, 1000);

#if DEBUG
            
foreach (byte b in bread)
            
{
                System.Diagnostics.Debug.WriteLine(b.ToString(
"X2"));
            }

#endif

            
if (bRet)
            
{
                
if (bread[0== 0xCC && bread[1== 0x30 && bread[2== 0x30)
                
{
                    
return true;
                }

                
else
                
{
                    
return false;
                }

            }

            
else
            
{
                
return false;
            }

        }

        
#endregion


        
发卡#region 发卡
        
public bool IssueCard(byte macno, string cardNo)
        
{
            
if (cardNo.Length < 8return false;

            
int retLen = 10;
            
byte[] bread;

            
//构建命令
            byte b1 = 0xAA;
            
byte b2 = 0x00;
            
byte b3 = 0x45;

            
byte cardNo1 = Convert.ToByte(cardNo.Substring(02), 16);
            
byte cardNo2 = Convert.ToByte(cardNo.Substring(22), 16);
            
byte cardNo3 = Convert.ToByte(cardNo.Substring(42), 16);
            
byte cardNo4 = Convert.ToByte(cardNo.Substring(62), 16);

            
byte xor = (byte)(cardNo1 ^ cardNo2 ^ cardNo3 ^ cardNo4);

            
byte[] bwrite = { b1, b2, b2, macno, macno, b3, b3, cardNo1, cardNo2, cardNo3, cardNo4, xor };
            
bool bRet = SendData(bwrite, retLen, out bread, 1000);

#if DEBUG
            
foreach (byte b in bwrite)
            
{
                System.Diagnostics.Debug.WriteLine(b.ToString(
"X2"));
            }

#endif

            
if (bRet)
            
{
                
if (bread[0== 0xCC && bread[1== 0x30 && bread[2== 0x30)
                
{
                    
return true;
                }

                
else
                
{
                    
return false;
                }

            }

            
else
            
{
                
return false;
            }

        }

        
#endregion


    }

}


using  System;
using  System.IO.Ports;
using  System.Text;

namespace  TripodDemo
{
    
/**//// <summary>
    
/// 产品Q01的长升级版本,大部分功能同Q01,但发卡模块增加有效期
    
/// </summary>

    public class SerialQ01A : SerialQ01
    
{
        
构造函数#region 构造函数
        
public SerialQ01A()
            : 
base()
        
{
            
//constructor
        }


        
public SerialQ01A(SerialPort sp)
            : 
base(sp)
        
{
            
//constructor
        }

        
#endregion


        
发卡#region 发卡
        
public bool IssueCard(byte macno, string cardNo, DateTime bTime, DateTime eTime)
        
{
            
if (cardNo.Length < 8return false;

            
int retLen = 10;
            
byte[] bread;

            
//构建命令
            byte b1 = 0xAA;
            
byte b2 = 0x00;
            
byte b3 = 0x45;

            
byte cardNo1 = Convert.ToByte(cardNo.Substring(02), 16);
            
byte cardNo2 = Convert.ToByte(cardNo.Substring(22), 16);
            
byte cardNo3 = Convert.ToByte(cardNo.Substring(42), 16);
            
byte cardNo4 = Convert.ToByte(cardNo.Substring(62), 16);

            
byte bYear = Convert.ToByte(bTime.Year.ToString(), 16);
            
byte bMonth = Convert.ToByte(bTime.Month.ToString(), 16);
            
byte bDay = Convert.ToByte(bTime.Day.ToString(), 16);

            
byte eYear = Convert.ToByte(eTime.Year.ToString(), 16);
            
byte eMonth = Convert.ToByte(eTime.Month.ToString(), 16);
            
byte eDay = Convert.ToByte(eTime.Day.ToString(), 16);

            
byte xor = (byte)(cardNo1 ^ cardNo2 ^ cardNo3 ^ cardNo4 ^ bYear ^ bMonth ^ bDay ^ eYear ^ eMonth ^ eDay);

            
byte[] bwrite = { b1, b2, b2, macno, macno, b3, b3, cardNo1, cardNo2, cardNo3, cardNo4, bYear, bMonth, bDay, eYear, eMonth, eDay, xor };
            
bool bRet = SendData(bwrite, retLen, out bread, 1000);

#if DEBUG
            
foreach (byte b in bwrite)
            
{
                System.Diagnostics.Debug.WriteLine(b.ToString(
"X2"));
            }

#endif

            
if (bRet)
            
{
                
if (bread[0== 0xCC && bread[1== 0x30 && bread[2== 0x30)
                
{
                    
return true;
                }

                
else
                
{
                    
return false;
                }

            }

            
else
            
{
                
return false;
            }

        }

        
#endregion

    }

}


源码示例: /Files/chenzz/TripodDemo_cs.zip
/Files/chenzz/TripodDemo.zip

转载于:https://www.cnblogs.com/chenzz/archive/2008/05/01/1178390.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CSerialPort First Version by Remon Spekreijse on 2000-02-08 http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm Second Version by mrlong on 2007-12-25 https://code.google.com/p/mycom/ 增加 ClosePort 增加 WriteToPort 两个方法 增加 SendData 与 RecvData 方法 by liquanhai on 2011-11-04 http://blog.csdn.net/liquanhai/article/details/4955253 增加 ClosePort 中交出控制权,防止死锁问题 by liquanhai on 2011-11-06 http://blog.csdn.net/liquanhai/article/details/6941574 增加 ReceiveChar 中防止线程死锁 by viruscamp on 2013-12-04 https://github.com/viruscamp/CSerialPort 增加 IsOpen 判断是否打开 修正 InitPort 中 parity Odd Even 参数取值错误 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的 增加用户消息编号自定义,方法来自 CnComm by itas109 on 2014-01-10 http://blog.csdn.net/itas109/article/details/18358297 解决COM10以上端口无法显示的问题 扩展可选择端口,最大值MaxSerialPortNum可以自定义 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。 by liquanhai on 2014-12-18 增加一些处理措施,主要是对减少CPU占用率 by itas109 on 2016-05-07 http://blog.csdn.net/itas109 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。 解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。 参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性 by itas109 on 2016-06-22 http://blog.csdn.net/itas109 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 解决ReceiveChar只能接收单个字符的问题。 by itas109 on 2016-06-29 http://blog.csdn.net/itas109 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。 增加IsThreadSuspend方法,用于判断线程是否挂起。 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收 by itas109 on 2016-08-02 http://blog.csdn.net/itas109 https://github.com/itas109 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL 改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭 改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED) 采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy 改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口 改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED) 加入viruscamp 取消对 MFC 的依赖 改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题 初始化默认波特率修改为9600 修复一些释放的BUG 规范了一些错误信息,参考winerror.h -- error code definitions for the Win32 API functions 删除SendData和RecvData方法 by itas109 on 2016-08-10 http://blog.csdn.net/itas109 https://github.com/itas109 改进ReceiveStr方法,comstat.cbInQue = 0xcccccccc的情况(如串口异常断开),会导致RXBuff初始化失败 by itas109 on 2017-02-14 http://blog.csdn.net/itas109 https://github.com/itas109 兼容ASCII和UNICODE编码 ReceiveStr函数中发送函数SendMessage的第二个参数采用结构体形式,包括portNr串口号和bytesRead读取的字节数,可以处理16进制的时候0x00截断问题 精简不必要的函数SendData和RecvData 尽量的取消对 MFC 的依赖,Hkey2ComboBox函数暂时保留 其他小问题修改 博客:blog.csdn.net/itas109 Email:itas109@qq.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值