⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mprcomm.cs

📁 WJ Communications RFID example code
💻 CS
字号:
//==========================================================================================
//
//	WJ.MPR.Reader.MPRComm
//	Copyright (c) 2006, WJ Communications, Inc.
//
//==========================================================================================
using System;
using System.Text;
using System.Threading;
using WJ.Serial;
using WJ.MPR.Util;

namespace WJ.MPR.Reader
{
	/// <summary>
	///	Responsible for handling messages bound for the MPR over a serial port.
	///		- Manages an underlying Serial Port object
	///		- Handles Serial Port Events
	///		- Generates Request Frames and parses Response Frames, and stores all frames for
	///			a single transaction in an MPRMsg.
	///		- Handles and reports Serial Port or MPR API errors.
	///		- Fires two events,
	///			1) BLData, when data arrives (always asynchronously) from the bootloader.
	///			2) IsOpenChanged, when the underlying serial port closes.
	///		- Performs the twiddling needed for Reseting the MPR and entering the bootloader.
	/// </summary>
	public class MPRComm : IDisposable
	{
		#region delegates and events
		/// <summary>
		/// Raised when the underlying serial port is opened or closed.
		/// </summary>
		public event BoolEventHandler IsOpenChanged;

		/// <summary>
		/// Fired when data is received from the MPR's bootloader.
		/// </summary>
		public event StringEventHandler BLDataArrived;
		#endregion

		#region Local Serial Port Properties
		
		/// <summary>
		/// create the port settings:
		///		default has no Handshake
		///		Bootloader uses XonXoff
		/// </summary>
		private DetailedPortSettings portSettings = new HandshakeNone();

		/// <summary>
		/// The underlying Serial Port which we'll use to talk to the MPR
		/// </summary>
		private Port SerialPort;

		/// <summary>
		/// The Baud Rate of the underlying Serial Port (default = 57600)
		/// </summary>
		public BaudRates BaudRate = BaudRates.CBR_57600;
		#endregion

		#region Message Timeout timer

		private System.Threading.Timer TimeOutTimer;

		/// <summary>
		/// Reset the TimeOutTimer, by changing it to expire, just once, in 750 milliseconds.
		/// </summary>
		public void KickTimer()
		{
			// 1 second comm timer that never repeats
			TimeOutTimer.Change(750, Timeout.Infinite);
		}

		/// <summary>
		/// Kill the TimeOutTimer, by changing it to never expire.
		/// </summary>
		public void KillTimer()
		{
			TimeOutTimer.Change(Timeout.Infinite, Timeout.Infinite);
		}

		/// <summary>
		/// The timer callback function.  Notify the current Msg that a comm timeout has occurred.
		/// </summary>
		/// <param name="ignore">Not used. Use null.</param>
		private void TimeOutFunction(object ignore)
		{
			if (CurrentMessage != null)
				CurrentMessage.Timeout();
			KillTimer();
		}
		#endregion

		#region Private Properties
		/// <summary>
		/// The currently active Message.
		/// </summary>
		private MPRMsg CurrentMessage;

		/// <summary>
		/// A temporary frame within which to store arriving bytes.
		/// </summary>
		private MPRFrame rxTmpFrame = new MPRFrame();
		#endregion

		BaudRates ConvertToBaudRateEnum(string BR)
		{
			BaudRates BaudRate = BaudRates.CBR_57600;	// The default
			try 
			{
				int BaudRate_int = int.Parse(BR);
				if (Enum.IsDefined(this.BaudRate.GetType(), BaudRate_int))
					BaudRate = (BaudRates)BaudRate_int;
			}
			catch { return BaudRates.CBR_57600; }	// If BR wasn't an string-encoded int, return the default BR, 57600 	

			return BaudRate;
		}

		/// <summary>
		/// Constructor that initializes, but doesn't open, the underlying serial port
		/// through which to communicate with an MPR.  
		/// </summary>
		/// <param name="SerialPortName">The "COMx" port to use.</param>
		/// <param name="BaudRate">The BaudRate to talk at, always 57600 for MPR series readers.</param>
		public MPRComm(string SerialPortName, string BaudRate)
		{
			TimeOutTimer = new System.Threading.Timer(new TimerCallback(TimeOutFunction), null, Timeout.Infinite, Timeout.Infinite);

			this.SerialPort = new Port(SerialPortName);

			this.BaudRate = ConvertToBaudRateEnum(BaudRate);

			SerialPort.Settings.BaudRate = this.BaudRate;
			SerialPort.SThreshold = 1;
			SerialPort.RThreshold = 1;

			// define event handlers
			SerialPort.DataReceived += new Port.CommEvent(SerialPort_DataReceived);
			SerialPort.TxDone += new Port.CommEvent(SerialPort_TxDone);
			SerialPort.OnError += new Port.CommErrorEvent(SerialPort_OnError);
			SerialPort.IsOpenChanged += new Port.CommChangeEvent(SerialPort_IsOpenChanged);
		}

		/// <summary>
		/// Try to open the underlying Serial Port.
		/// </summary>
		/// <returns>Whether or not the port was successfully opened.</returns>
		public bool Open()
		{
			try 
			{
				SerialPort.Open();
			}
			catch (CommPortException)
			{
				// If Port cannot be opened for some reason, 
				// a CommPortException will be thrown, ignore it,
				// try to close the port and return false
				SerialPort.Close();
				return false;
			}
			return SerialPort.IsOpen;
		}

		/// <summary>
		/// Just map the underlying ports IsOpen property.
		/// </summary>
		public bool IsOpen { get { return SerialPort.IsOpen; } }

		/// <summary>
		/// Close the underlying serial port immediately...
		/// </summary>
		public void Close() { 
			SerialPort.Close();
		}

		/// <summary>
		/// Build an MPRMsg using command and parameters, 
		/// send the request frame, and wait for and receive
		/// response packets.
		/// </summary>
		/// <param name="command">The command to send.</param>
		/// <param name="parameters">The parameters for the command to send.</param>
		/// <returns>The MPRMsg will contain the request and response frames for further processing.</returns>
		public MPRMsg Send(CmdCode command, byteList parameters)
		{
			lock(this) 
			{
				try 
				{
					// First Convert the msg parameter to an MPRMessage (if possible)
					CurrentMessage = new MPRMsg(command, parameters);

					if (!SendFrame(CurrentMessage.TxFrame))
						CurrentMessage.CommError("Send Frame Failed.");

					CurrentMessage.Receive();
					KillTimer();
				}
				catch (Exception ex) 
				{
					if (CurrentMessage != null)
						CurrentMessage.CommError(ex.Message);
				}
				return CurrentMessage;
			}
		}

		/// <summary>
		/// Send a request frame to the MPR via the Serial Port
		/// Kicks the receiver timer after sending a frame
		/// </summary>
		/// <param name="txFrame">a Frame to send</param>
		/// <returns>true if port is still open at exit of function.
		///	false if port==null, or port isn't open.</returns>
		private bool SendFrame(MPRFrame txFrame)
		{
			// If null or not open return false
			if ((SerialPort == null) || (!SerialPort.IsOpen))
				return false;

			SerialPort.Output = txFrame.ToArray();						// Send the bytes
			KickTimer();

			return SerialPort.IsOpen;
		}

		/// <summary>
		/// Send one line of text
		/// </summary>
		/// <param name="line"></param>
		/// <returns>Whether the serial port is still open after sending the line.</returns>
		public bool SendBLLine(string line)
		{
			ASCIIEncoding encoding = new ASCIIEncoding();

			SerialPort.Output = encoding.GetBytes(line);
			//Console.WriteLine("Sent: {0}", line);

			return SerialPort.IsOpen;
		}

		/// <summary>
		/// Just send raw bytes out the serial port.
		/// </summary>
		/// <param name="payload"></param>
		public void SendBytes(byte[] payload)
		{
			SerialPort.Output = payload;
		}
	
		#region IDisposable Members

		/// <summary>
		/// Free resources:
		///		Dispose the underlying Serial port.
		/// </summary>
		public void Dispose()
		{
			SerialPort.Dispose();
		}

		#endregion

		#region SerialPort Event Handlers
		
		private void SerialPort_DataReceived()
		{
			byte[] c = SerialPort.Input;

			//ASCIIEncoding encoding = new ASCIIEncoding();
			//Console.Write("{0}",encoding.GetString(c, 0, c.Length));

			if (rxTmpFrame != null)
				foreach(byte b in c) 
				{
					rxTmpFrame.Add(b);
					if (rxTmpFrame.CompleteFrame) 
					{
						// Let Msg class determine whether to kick or kill Timer
						CurrentMessage.AddRxFrame(rxTmpFrame);
						rxTmpFrame = new MPRFrame();
					}
				}
		}

		private void SerialPortBL_DataReceived()
		{
			byte[] c = SerialPort.Input;

			// Remove '\0' characters from byte array
			for (int i=0; i < c.Length; i++)
				if (c[i] == 0)
					c[i] = (byte)' ';
			
			ASCIIEncoding encoding = new ASCIIEncoding();
			string s = encoding.GetString(c, 0, c.Length);
			//s.Replace("\0","\n");
			//Console.Write("{0}",encoding.GetString(c, 0, c.Length));
			if (BLDataArrived != null) 
				BLDataArrived(s);
		}


		/// <summary>
		/// Not used:  Fired when the Serial Port is done transmitting the whole Input buffer.
		/// </summary>
		private void SerialPort_TxDone()
		{
			// Message just sent, start Receive Message timer
//			KickTimer();
		}


		/// <summary>
		/// Fired when the Serial Port detects an error condition.
		/// </summary>
		/// <param name="Description">A string describing the error condition.</param>
		private void SerialPort_OnError(string Description)
		{
			KillTimer();

			if (CurrentMessage != null)
				CurrentMessage.CommError(Description);
			rxTmpFrame = new MPRFrame();
		}


		/// <summary>
		/// Just a thin wrapper around the underlying Port's
		/// IsOpenChanged event
		/// </summary>
		/// <param name="NewState">true if Serial Port is now open; otherwise false.</param>
		private void SerialPort_IsOpenChanged(bool NewState)
		{
			if (!NewState && (CurrentMessage != null))
				CurrentMessage.CommError("COM Port disconnected");

			if (!NewState)
				if (IsOpenChanged != null) 
					IsOpenChanged(NewState);
		}
		#endregion

		/// <summary>
		/// Returns a string with the serial port and baud rate : "COMx 57600".
		/// </summary>
		public string PortSetting
		{
			get { return SerialPort.PortName.Trim(new char[] {'\\','.'}) + " " + SerialPort.Settings.BaudRate.ToString().Remove(0,4); }
		}

		/// <summary>
		/// Setup Event Handlers and Port Settings for Bootloader communications mode.
		/// </summary>
		public void blModeSetup()
		{
			// Remove any previously assigned Data Received Handlers
			SerialPort.DataReceived -= new Port.CommEvent(SerialPort_DataReceived);
			SerialPort.DataReceived -= new Port.CommEvent(SerialPortBL_DataReceived);

			// Create new Basic Port Settings
			BasicPortSettings PBS = new BasicPortSettings();
			PBS.BaudRate = BaudRates.CBR_115200;
			PBS.ByteSize = 8;
			PBS.Parity = Parity.none;
			PBS.StopBits = StopBits.one;

			// Create a new HandshakeXonXoff DetailedPortSettings
			DetailedPortSettings DPS = new HandshakeXonXoff();
			DPS.BasicSettings = PBS;
			SerialPort.DetailedSettings = DPS;

			// Attach Serial Port Data Received Handler to the BL handler
			SerialPort.DataReceived += new Port.CommEvent(SerialPortBL_DataReceived);
		}

		/// <summary>
		/// Setup Event Handlers and Port Settings for Application (normal) communications mode.
		/// </summary>
		public void appModeSetup()
		{
			// Remove any previously assigned Data Received Handlers
			SerialPort.DataReceived -= new Port.CommEvent(SerialPort_DataReceived);
			SerialPort.DataReceived -= new Port.CommEvent(SerialPortBL_DataReceived);

			BasicPortSettings PBS = new BasicPortSettings();
			PBS.BaudRate = BaudRates.CBR_57600;
			PBS.ByteSize = 8;
			PBS.Parity = Parity.none;
			PBS.StopBits = StopBits.one;

			// Create a new HandshakeNone DetailedPortSettings
			DetailedPortSettings DPS = new HandshakeNone();
			DPS.BasicSettings = PBS;
			SerialPort.DetailedSettings = DPS;

			// Attach Serial Port Data Received Handler to the Application handler
			SerialPort.DataReceived += new Port.CommEvent(SerialPort_DataReceived);
		}


		/// <summary>
		/// Hard reboot the MPR's processor.
		/// Resetting can bring the MPR up in one of two programs.
		///		If the Bootloader line is high, we enter the bootloader.
		///		Else, the normal Application is started.
		///	The Application uses no Handshaking, 57600 baud, and communicates through MPRMsg Frames.
		///	The Bootloader uses XonXoff Handshaking, 115200 baud, and communicates through ASCII text.
		///	The two different types of message require different Message handlers 
		///	(SerialPort_DataRecevied and SerialPortBL_DataRecevied).
		///	
		///	This Method waits 100 msec after toggling the Reset Line, for the MPR to reboot.
		///	
		///	There are two ways of communicating with the MPR, serial mode and PC Card mode.
		///
		///	In serial mode, the BOOTLOAD and RESET lines are brought out to the 68-pin connector,
		///	and must be toggled with dedicated hardware lines.
		///
		///	In PC Card mode, the BOOTLOAD and RESET lines are internal to the MPR and are toggled
		///	by special commands.  These special commands are accessed by changing the serial port
		///	settings to {57600 BPS, 5 Data bits, 1.5 stop bits, no parity}.  
		///	The reset line is then accessed as bit 4 (0x10). 
		///	The BOOTLOAD line is then accessed as bit 0 (0x01).
		///	
		///	This MPRComm.ResetMPR Method only works in the PC Card Mode.
		///	
		/// </summary>
		/// <param name="BootLoader">Whether the MPR should start in the boot loader (true) 
		/// or the application (false).</param>
		public void ResetMPR(bool BootLoader)
		{
			try 
			{
				BasicPortSettings PBS_save = SerialPort.Settings;	// Save a copy of the Serial Port settings
				BasicPortSettings PBS = new BasicPortSettings();
				PBS.BaudRate = BaudRates.CBR_57600;
				PBS.ByteSize = 5;
				PBS.Parity = Parity.none;
				PBS.StopBits = StopBits.onePointFive;

				// Set the special serial port settings.
				SerialPort.Settings = PBS;

				// Send either Reset & Bootload pulses, or just Reset pulse, depending on BootLoader parameter
				byte[] Data;
				if (BootLoader)
					Data = new byte[] {0x11, 0x01};		// {Reset | Bootload, ~Reset | Bootload }
				else
					Data = new byte[] {0x10, 0x00};		// {Reset | ~Bootload, ~Reset | ~Bootload }

				// Send the Reset message
				SerialPort.Output = Data;

				// Restore the serial port settings
				SerialPort.Settings = PBS_save;

				if (BootLoader) 
				{
					blModeSetup();
				} 
				else 
				{
					appModeSetup();
				}

				// Sleep for 100 msec to give MPR time to reboot
				Thread.Sleep(100);
			}
			catch {}
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -