Web浏览器读写NFC Ntag标签

 本示例使用的发卡器:RS232串口USB转COM读写器IC卡发卡器WEB浏览器二次开发JS编程SDK-淘宝网 (taobao.com)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Web Serial串口读写器Ntag卡示例 </title>
	<script>		
		window.onload = function() {
   			document.getElementById('butt_openserial').hidden=true;
			document.getElementById('butt_closeserial').hidden=true;
			document.getElementById('dispauthkey').hidden=true;
			document.getElementById('authkey0').hidden=true;
		}
		
		if ('serial' in navigator){
			
		}else{
			alert('您的浏览器不支持 Web Serial API,暂无法使用以下功能!');
		}		
		
		navigator.serial.onconnect =function event(){
			console.log("Serial port connected: ", event.target);
		}
		
		navigator.serial.ondisconnect =function event(){
			console.log("Serial port disconnected: ", event.target);
		}
		
		var BLOCK0_EN = 0x01;//读第一块的(16个字节)
        var BLOCK1_EN = 0x02;//读第二块的(16个字节)
        var BLOCK2_EN = 0x04;//读第三块的(16个字节)
        var NEEDSERIAL = 0x08;//仅读指定序列号的卡
        var EXTERNKEY = 0x10;//用明码认证密码,产品开发完成后,建议把密码放到设备的只写区,然后用该区的密码后台认证,这样谁都不知道密码是多少,需要这方面支持请联系
        var NEEDHALT = 0x20; //读/写完卡后立即休眠该卡,相当于这张卡不在感应区。要相重新操作该卡必要拿开卡再放上去
						
		var port = null;
        var reader = null;
        var reading = false;
        const getdata=new Uint8Array(1000);     //接收串口返回的数据
        var DataPoint=0;                        //接收数据指针
        var SendCode=0;                         //已发送的指令代码

        function isUIntNum(val) {
            var testval = /^\d+$/; // 非负整数
            return (testval.test(val));
        }

        function isHex(val) {
            var testval = /^(\d|[A-F]|[a-f])+$/; // 十六进制数判断
            return (testval.test(val));
        }
		
		
		async function SelectSerial(){
			try{
				port =await navigator.serial.requestPort();  // 弹出系统串口列表对话框,选择一个串口进行连接
				ports =await navigator.serial.getPorts();    // 获取已连接的授权过的设备列表
				document.getElementById('butt_openserial').hidden=false;				
			}
			catch (e)
			{
				console.log(e);
			}
		}
		
		function updateInputData(data) {
            let array = new Uint8Array(data); // event.data.buffer就是接收到的inputreport包数据了
            //let hexstr = "";					
			for (const data of array) {
                //hexstr += (Array(2).join(0) + data.toString(16).toUpperCase()).slice(-2) + " "; // 将字节数据转换成(XX )形式字符串
				getdata[DataPoint]=data;
                DataPoint=DataPoint+1;
            }
			
		    var crc=0;
		    for(i=1;i<DataPoint;i++){   //校验接收数据,同时也解决数据分包上传的问题
		        crc=crc^getdata[i];
		    }
		    if (crc==0 && DataPoint>1){                
		        let hexstr = "";
		        for (i=0;i<DataPoint;i++){
		            hexstr=hexstr+getdata[i].toString(16).padStart(2, '0').toUpperCase()+" ";
		        }
		        ReceiveData.value += hexstr;		        
				
				var dispstr="";
				var cardnohex="";
				var datahex="";
				switch (SendCode) {
				    case 1:   //驱动发卡器响声的回应
				        dispstr = "发卡器已执行响声指令!" ; 		
				        label_disp.innerText=dispstr;
				        break;

				    case 6:    //读Ntag卡的回应
				        switch (getdata[1]){
				            case 0x00:
				                for (i=2;i<=8;i++){cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();}
				                for (i=11;i<DataPoint-1;i++){datahex=datahex+getdata[i].toString(16).padStart(2, '0').toUpperCase()+" ";}
				                dispstr= "读Ntag卡成功,卡号:" + cardnohex ;				                
				                RWdata.value=datahex;
				                break;
				            case 0x08:
				                dispstr= "读卡失败,未寻到卡!"				                
				                break;
				            case 0x09:
				                dispstr= "读卡失败!两张以上卡片同时在感应区发生冲突!"				                
				                break;
				            case 0x0c:
				                for (i=2;i<=8;i++){cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();}
				                dispstr= "卡密码认证失败,卡号:" + cardnohex +",读取块数据失败!";				                
				                break;
				            case 0x0d:
				                for (i=2;i<=8;i++){cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();}
				                dispstr= "可能要带密码操作,卡号:" + cardnohex +",读取块数据失败!";				                
				                break;
				            default:
				                dispstr= "读卡失败,返回错误代码:"+getdata[1].toString();
				        }
				        label_disp.innerText=dispstr;
				        break;

				    case 7:    //写Ntag卡的回应
				    case 8:    //初始化Ntag卡的回应
				        switch (getdata[1]){
				            case 0x00:
				                for (i=2;i<=8;i++){cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();}				                
				                dispstr= "写Ntag卡成功,卡号:" + cardnohex ;				                
				                break;
				            case 0x08:
				                dispstr= "读卡失败,未寻到卡!"				                
				                break;
				            case 0x09:
				                dispstr= "写卡失败!两张以上卡片同时在感应区发生冲突!"				                
				                break;
				            case 0x0c:
				                for (i=2;i<=8;i++){cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();}
				                dispstr= "卡密码认证失败,卡号:" + cardnohex +",写块数据失败!";				                
				                break;
				            case 0x0e:
				                for (i=2;i<=8;i++){cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();}
				                dispstr= "可能要带密码操作,卡号:" + cardnohex +",写块数据失败!";				                
				                break;
				            default:
				                dispstr= "写卡失败,返回错误代码:"+getdata[1].toString();
				        }
				        label_disp.innerText=dispstr;
				        break;
				}		        
				DataPoint=0;        //数据接收指针置0
		    }
        }		
		
		async function listenReceived(){
			if (reading){
				console.log("On reading.");
                return;
			}
			reading=true;
			
			while (port.readable && reading) {
                reader = port.readable.getReader();
                try {
                        updateInputData(value);
                } catch (e) {
                    alert(e);
                } finally {
                    reader.releaseLock();
                }
            }
			
			await port.close(); // 关闭串口
            port = null;
            alert("串口已关闭!");
		}
				 
		async function OpenSerial(){
			if (port==null){
				alert('请先选择要操作的串口号!');
				return;
			}else{
				document.getElementById('butt_closeserial').hidden=false;	
				var baudSelected = parseInt(document.getElementById("select_btn").value);
				await port.open({
					baudRate: baudSelected,					
					});	
				listenReceived();	
				alert('串口打开成功!');							
			}			
		}
		
		async function CloseSerial(){
			if ((port == null) || (!port.writable)) {
                alert("请选择并打开与发卡器相连的串口!");
       			return;
            }
			
			if (reading) {
				reading = false;
				reader?.cancel();
			}	
			document.getElementById('butt_openserial').hidden=true;
			document.getElementById('butt_closeserial').hidden=true;		
		}
		
		function selecheckauthkey(){			
			if (checkauth.checked){				
                document.getElementById('dispauthkey').hidden=false;
				document.getElementById('authkey0').hidden=false;
			}else{
				document.getElementById('dispauthkey').hidden=true;
				document.getElementById('authkey0').hidden=true;
			}			
		}
		
		async function beep(){
			if ((port == null) || (!port.writable)) {
                alert("请选择并打开与发卡器相连的串口!");
        		return;
        	}
			var beepdelay=parseInt(document.getElementById("beepdelay").value);
			const outputData = new Uint8Array(5);
			outputData[0]=0x03;     
			outputData[1]=0x0f;   
			outputData[2]=beepdelay % 256;
			outputData[3]=beepdelay / 256;
			outputData[4]=outputData[1] ^ outputData[2] ^outputData[3];
			
			var sendhex="";
			for(i=0;i<5;i++){
				sendhex=sendhex+outputData[i].toString(16).padStart(2, '0').toUpperCase()+" ";
			}			
			SendData.value=sendhex;
			ReceiveData.value="";
			SendCode=1;
			DataPoint=0;
            const writer = port.writable.getWriter();
	        await writer.write(outputData);           // 发送数据
    	    writer.releaseLock();
		}
		
		async function piccinit_ntag(){
			if ((port == null) || (!port.writable)) {
                alert("请选择并打开与发卡器相连的串口!");
        		return;
			}

			myctrlword = 0; 							//指定控制字,无需密码为0,当需要密码时为EXTERNKEY;			
			mypiccserial = "00000000000000";  			//指定序列号,00000000000000 表示任意 NTAG卡。			
			if (checkauth.checked) {			     	//指定密码,NTAG21x卡密码为4个字节,卡出厂时密码功能不启用,这样无需密码也能读写卡
			    myctrlword = EXTERNKEY;              	//指定控制字,无需密码为0,当需要密码时为EXTERNKEY;
			    mypicckey = authkey0.value.trim();
			    if (!isHex(mypicckey) || mypicckey.length!=8) {
			        alert( "卡片认证密钥输入错误,请输入8位16进制密钥!");
			        authkey0.focus();
			        authkey0.select();
			        return;
			    }
			}
			else {
			    mypicckey = "00000000";               	
			}
						
		    //数据准备
			if (selonoff.selectedIndex == 0) {			//开启密码保护功能,写保护功能生效,但读保护需要下面的数据设定                
			    newkeystr = newkey.value.trim();		//取新密码
			    if (!isHex(newkeystr) || newkeystr.length != 8) {
			        alert( "新密钥输入错误,请输入8位16进制新密钥!");
			        newkey.focus();
			        newkey.select();
			        return;
			    }

			    strls1 = protectpageno.value.trim();//起始保护页号
			    if (!isUIntNum(strls1)) {
			        alert("起始保护页号输入错误!");
			        protectpageno.focus();
			        protectpageno.select();
			        return;
			    }
			    strls1 = "0" + parseInt(strls1).toString(16);
			    beginpage = strls1.substring(strls1.length - 2);	
							                
			    //计数器
			    strls1 = keyerrortimes.value.trim();//允许密码错误次数
			    if (!isUIntNum(strls1)) {
			        alert("允许密码错误次数输入错误!");
			        protectpageno.focus();
			        protectpageno.select();
			        return;
			    }
			    i = parseInt(strls1);
			    i = i % 8;
			    if (checkreadon.checked) {
			        i = i + 128;
			    }
			    strls1 = "0" + i.toString(16);
			    authfail = strls1.substring(strls1.length - 2);
				
			    packstr = packcode.value.trim();		//取PACK码
			    if (!isHex(packstr) || packstr.length != 4) {
			        alert( "PACK密钥确认码输入错误,请输入4位16进制PACK密钥确认码!");
			        packcode.focus();
			        packcode.select();
			        return;
			    }				
				
			    mypiccdata = "000000";
			    mypiccdata = mypiccdata +beginpage;
			    mypiccdata = mypiccdata + authfail;
			    mypiccdata = mypiccdata + "000000";
			    mypiccdata = mypiccdata + newkeystr;         //4字节新密码
			    mypiccdata = mypiccdata + packstr;			 //2字节PACK确认码	
			    mypiccdata = mypiccdata + "0000";
				
			    myctrlword = myctrlword + 0x01; //更新控制字
			    myctrlword = myctrlword + 0x02; //更新控制字
			    myctrlword = myctrlword + 0x04; //更新控制字
			}
			else {
			    mypiccdata = "000000FF";		//MIRROR,RFUI,MIRROR_PAGE,AUTH0
			    myctrlword = myctrlword + 0x01; //更新控制字
			    mypiccdata = mypiccdata + "000000000000000000000000";
			    myctrlword = myctrlword + 0x02; //更新控制字
			}
			
			const outputData = new Uint8Array(31);
			outputData[0]=0x1d;            //指令数据长度       
			outputData[1]=0x16;            //功能码
			outputData[2]=myctrlword;      //控制位
			for (i=0;i<7;i++){             //7字节本次操作卡UID,7字节全部取0表示可操作任意ntag标签
			    outputData[3+i]=parseInt(mypiccserial.substr(i*2,2),16);
			}
			for (i=0;i<4;i++){             //4字节卡片认证密钥
			    outputData[10+i]=parseInt(mypicckey.substr(i*2,2),16);
			}
			for (i=0;i<16;i++){            //16字节初化数据 
			    outputData[14+i]=parseInt(mypiccdata.substr(i*2,2),16);
			}

			var crc=0;
			for (i=1;i<30;i++){crc=crc^outputData[i];}
			outputData[30]=crc;	          //指令信息累加和校验位
			
			var sendhex="";
			for(i=0;i<31;i++){
			    sendhex=sendhex+outputData[i].toString(16).padStart(2, '0').toUpperCase()+" ";
			}			
			SendData.value=sendhex;
			ReceiveData.value="";
			
			SendCode=8;
			DataPoint=0;
			var label_disp = document.getElementById('label_disp');
			label_disp.innerText = "";
								
			const writer = port.writable.getWriter();
			await writer.write(outputData);           // 发送数据
			writer.releaseLock();			
		}
		
		async function readcard_ntag(){
			if ((port == null) || (!port.writable)) {
                alert("请选择并打开与发卡器相连的串口!");
        		return;
        	}			
			
			mypiccserial = "00000000000000";         	//指定序列号,未知卡序列号时可指定为14个0,因为NTAG21x卡是7个字节的卡序列号            
			if (checkauth.checked) {			     	//指定密码,NTAG21x卡密码为4个字节,卡出厂时密码功能不启用,这样无需密码也能读写卡
			    myctrlword = EXTERNKEY;              	//指定控制字,无需密码为0,当需要密码时为EXTERNKEY;
			    mypicckey = authkey0.value.trim();
			    if (!isHex(mypicckey) || mypicckey.length!=8) {
			        alert( "卡片认证密钥输入错误,请输入8位16进制密钥!");
			        authkey0.focus();
			        authkey0.select();
			        return;
			    }
			}
			else {
			    mypicckey = "00000000";
			    myctrlword = 0;                      	//指定控制字,无需密码为0,当需要密码时为EXTERNKEY;
			}

			myblockaddr = ntagstartno.value.trim();  	//读写起始页
			if (!isUIntNum(myblockaddr)) {
			    alert( "读写起始页输入错误!");
			    ntagstartno.focus();
			    ntagstartno.select();
			    return;
			}

			myblocksize = ntagpagenumber.value.trim();	//读写页数
			if (!isUIntNum(myblocksize)) {
			    alert( "读写页数输入错误!");
			    ntagpagenumber.focus();
			    ntagpagenumber.select();
			    return;
			}else if(myblocksize>12){
			    alert( "每次最多读取12块数据!");
			    ntagpagenumber.focus();
			    ntagpagenumber.select();
			    ntagpagenumber.value="12";
			    return;
			}

			const outputData = new Uint8Array(17);
			outputData[0]=0x0f;            //指令数据长度       
			outputData[1]=0x1b;            //功能码
			outputData[2]=myctrlword;      //控制位
			for (i=0;i<7;i++){             //7字节本次操作卡UID,7字节全部取0表示可操作任意ntag标签
			    outputData[3+i]=parseInt(mypiccserial.substr(i*2,2),16);
			}
			for (i=0;i<4;i++){             //4字节卡片认证密钥
			    outputData[10+i]=parseInt(mypicckey.substr(i*2,2),16);
			}
			outputData[14]=myblockaddr;    //读卡起始块
			outputData[15]=myblocksize;    //读卡总块数
			
			var crc=0;
			for (i=1;i<16;i++){
			    crc=crc^outputData[i];
			}
			outputData[16]=crc;	          //指令信息累加和校验位
			
			var sendhex="";
			for(i=0;i<17;i++){
			    sendhex=sendhex+outputData[i].toString(16).padStart(2, '0').toUpperCase()+" ";
			}			
			SendData.value=sendhex;
			ReceiveData.value="";
			RWdata.value="";
			SendCode=6;
			DataPoint=0;
			var label_disp = document.getElementById('label_disp');
			label_disp.innerText = "";
								
			const writer = port.writable.getWriter();
			await writer.write(outputData);           // 发送数据
			writer.releaseLock();			
		}
		
		async function writecard_ntag(){
			if ((port == null) || (!port.writable)) {
                alert("请选择并打开与发卡器相连的串口!");
        		return;
        	}			
			
			mypiccserial = "00000000000000";         	//指定序列号,未知卡序列号时可指定为14个0,因为NTAG21x卡是7个字节的卡序列号            
			if (checkauth.checked) {			     	//指定密码,NTAG21x卡密码为4个字节,卡出厂时密码功能不启用,这样无需密码也能读写卡
			    myctrlword = EXTERNKEY;              	//指定控制字,无需密码为0,当需要密码时为EXTERNKEY;
			    mypicckey = authkey0.value.trim();
			    if (!isHex(mypicckey) || mypicckey.length!=8) {
			        alert( "卡片认证密钥输入错误,请输入8位16进制密钥!");
			        authkey0.focus();
			        authkey0.select();
			        return;
			    }
			}
			else {
			    mypicckey = "00000000";
			    myctrlword = 0;                      	//指定控制字,无需密码为0,当需要密码时为EXTERNKEY;
			}

			myblockaddr = ntagstartno.value.trim();  	//读写起始页
			if (!isUIntNum(myblockaddr)) {
			    alert( "读写起始页输入错误!");
			    ntagstartno.focus();
			    ntagstartno.select();
			    return;
			}

			myblocksize = ntagpagenumber.value.trim();	//读写页数
			if (!isUIntNum(myblocksize)) {
			    alert( "读写页数输入错误!");
			    ntagpagenumber.focus();
			    ntagpagenumber.select();
			    return;
			}else if(myblocksize>11){
			    alert( "每次最多写入11块数据!");
			    ntagpagenumber.focus();
			    ntagpagenumber.select();
			    ntagpagenumber.value="11";
			    return;
			}

			var datalen=myblocksize*4;
		    
			mypiccdata = RWdata.value.trim();            //写卡数据
			mypiccdata=mypiccdata.replace(/\s/g, "");
			if (!isHex(mypiccdata)) {
			    alert( "写卡数据输入错误,请输入"+(datalen*2).toString()+"位16进制写卡数据!");
			    RWdata.focus();
			    RWdata.select();
			    return;
			}else if(mypiccdata.length<datalen*2){
			    if (confirm("写卡数据不足,是否要后面补0写入?")) {      
			        while (mypiccdata.length<datalen*2){
			            mypiccdata=mypiccdata+"0";
			        }
			    }else{
			        return;
			    }	
			}

			const outputData = new Uint8Array(datalen+17);
			outputData[0]=15+datalen;      //指令数据长度       
			outputData[1]=0x1c;            //功能码
			outputData[2]=myctrlword;      //控制位
			for (i=0;i<7;i++){             //7字节本次操作卡UID,7字节全部取0表示可操作任意ntag标签
			    outputData[3+i]=parseInt(mypiccserial.substr(i*2,2),16);
			}
			for (i=0;i<4;i++){             //4字节卡片认证密钥
			    outputData[10+i]=parseInt(mypicckey.substr(i*2,2),16);
			}
			outputData[14]=myblockaddr;    //写卡起始块
			outputData[15]=myblocksize;    //写卡总块数
			for  (i=0;i<datalen;i++){      //写卡数据
			    outputData[16+i]=parseInt(mypiccdata.substr(i*2,2),16);
			}

			var crc=0;                     
			for (i=1;i<16+datalen;i++){
			    crc=crc^outputData[i];
			}
			outputData[16+datalen]=crc;	   //指令信息累加和校验位
			
			var sendhex="";
			for(i=0;i<17+datalen;i++){
			    sendhex=sendhex+outputData[i].toString(16).padStart(2, '0').toUpperCase()+" ";
			}			
			SendData.value=sendhex;
			ReceiveData.value="";
			
			SendCode=7;
			DataPoint=0;
			var label_disp = document.getElementById('label_disp');
			label_disp.innerText = "";
								
			const writer = port.writable.getWriter();
			await writer.write(outputData);           // 发送数据
			writer.releaseLock();								
		}
				
		
	</script>
    
	<style>
		th {
		  font-family:楷体;
		  background-color:#F6FAFF;		
		  color:blue;
		}
		td {
		  font-family:楷体;
		  background-color:#F6FAFF;		
		}  
    </style>    
    
</head>

<body>
<table width="950" height="428"  align="center">
  <tr>
    <td width="120" height="50">    
      <input  name="btnSelect"  type="submit" id="btnSelect" style="width:100%" onclick="SelectSerial()" value="选择串口" />
    </td>
    <td width="800">波特率:<label for="select_btn"></label>
        <select name="select_btn" id="select_btn">
          <option>1200</option>
          <option>4800</option>
          <option>9600</option>
          <option>14400</option>
          <option selected="selected">19200</option>
          <option>38400</option>
          <option>43000</option>
          <option>57600</option>
          <option>115200</option>
          <option>128000</option>
          <option>230400</option>
		  <option>256000</option>
		  <option>460800</option>
		  <option>921600</option>
		  <option>1382400</option>
        </select>
        &nbsp;数据位:
        <select name="select_btn2" id="select_data">
          <option>8</option>
          <option>7</option>
          <option>6</option>
          <option>5</option>
        </select>
        &nbsp;停止位:
        <select name="select_btn3" id="select_stop">
          <option>1</option>
          <option>1.5</option>
          <option>2</option>
        </select>
        &nbsp;校验位:
      <select name="select_btn4" id="select_mark">
          <option>None  无</option>
          <option>Odd   奇</option>
		  <option>Even  偶</option>
		  <option>Mask  常1</option>
		  <option>Space 常0</option>
        </select>
        &nbsp;<input  name="butt_openserial"  type="submit" id="butt_openserial" style="width:80px" onclick="OpenSerial()" value="打开串口" />
        <input  name="butt_closeserial"  type="submit" id="butt_closeserial" style="width:80px" onclick="CloseSerial()" value="关闭串口" />
	</td>
  </tr>
  
  <tr>
    <td height="36" >
      <input name="butt_beep"  type="submit" id="butt_beep" style="width:100%" onclick="beep()" value="驱动发卡器响声" />
    </td>
    <td>响声延时:
    <input style="color:blue;text-align:center;" name="beepdelay" type="text" id="beepdelay" value="30" size="5" maxlength="4" onkeyup="this.value=this.value.replace(/\D/g,'')"/>
    毫秒</td>
  </tr>
  
  <tr>
    <td height="36">&nbsp;</td>
    <td><label style="color:blue;" name="label_disp" id="label_disp"></label></td>
  </tr>
  
  <tr>
    <td height="36">&nbsp;</td>
    <td>
      <input type="checkbox" name="checkauth" id="checkauth" onchange="selecheckauthkey()"/>      
      选择先认证卡片密钥再继续以下的操作
      <label name="dispauthkey" id="dispauthkey">,16进制卡片认证密钥:</label>
    <input style="color:blue;text-align:center;" name="authkey0" type="text" id="authkey0" value="12345678" size="8" maxlength="8" onkeyup="this.value=this.value.replace(/[^0-9a-fA-F]/g,'')"/>
       
    </td>
  </tr>
  
  <tr>
    <td height="69"><input style="width:120px" name="butt_piccinit_ntag" type="submit" id="butt_piccinit_ntag" onclick="piccinit_ntag()" value="初始化Ntag卡" /></td>
    <td><p>
      <label for="rwtext"></label>
      <select style="color:blue;" name="selonoff" id="selonoff">
        <option>开启卡片密钥保护功能</option>
        <option selected="selected">取消卡片密钥保护功能</option>
      </select>
    ,从:
    <input style="color:blue;text-align:center;" name="protectpageno" type="text" id="protectpageno" value="20" size="4" maxlength="4" onkeyup="this.value=this.value.replace(/\D/g,'')"/>
    页开始有密钥保护功能,
    <input type="checkbox" name="checkreadon" id="checkreadon" />
    选择开启读操作密钥保护。</p>
      <p>新密钥:
        <input style="color:blue;text-align:center;" name="newkey" type="text" id="newkey" value="12345678" size="8" maxlength="8" onkeyup="this.value=this.value.replace(/[^0-9a-fA-F]/g,'')"/>
      ,允许密钥认证失败次数:
      <input style="color:blue;text-align:center;" name="keyerrortimes" type="text" id="keyerrortimes" value="0" size="2" maxlength="2" onkeyup="this.value=this.value.replace(/\D/g,'')"/>
      ,PACK密钥确认码:
      <input style="color:blue;text-align:center;" name="packcode" type="text" id="packcode" value="1234" size="4" maxlength="4" onkeyup="this.value=this.value.replace(/[^0-9a-fA-F]/g,'')"/>
      </p>
    <p style="color:red;">警告:当密钥认证失败次数取值0表示不限制次数,认证密钥操作失败大于设置值时卡片将会报废!</p></td>
  </tr>  
  
  <tr>
    <td height="36"><p>
      <input style="width:120px" name="butt_readcard_ntag" type="submit" id="butt_readcard_ntag" onclick="readcard_ntag()" value="轻松读Ntag卡" />
    </p>
    <p>&nbsp;</p>
    <p>
      <input style="width:120px" name="butt_writecard_ntag" type="submit" id="butt_writecard_ntag" onclick="writecard_ntag()" value="轻松写Ntag卡" />
    </p></td>
    <td><p>读写起始页:
      <input style="color:blue;text-align:center;" name="ntagstartno" type="text" id="ntagstartno" value="4" size="4" maxlength="4" onkeyup="this.value=this.value.replace(/\D/g,'')"/>
    ,读写页数:
    <input style="color:blue;text-align:center;" name="ntagpagenumber" type="text" id="ntagpagenumber" value="10" size="2" maxlength="2" onkeyup="this.value=this.value.replace(/\D/g,'')"/>
    ,每次最多读12页、写11页。</p>
      <p>
        <textarea style="width:800px;color:red;font-family:楷体;" name="RWdata" id="RWdata" cols="100" rows="4" ></textarea>
    </p></td>
  </tr>    
  
  <tr>
    <td height="70" scope="row"><p align="center">发送的数据</p></td>
    <td><textarea style="width:800px;color:blue;" name="SendData" id="SendData" cols="100" rows="4" ></textarea></td>
  </tr>
  
  <tr>
    <td height="70" scope="row"><p align="center">接收的数据</p></td>
    <td><textarea style="width:800px" name="ReceiveData" id="ReceiveData" cols="100" rows="4" ></textarea></td>
  </tr>
</table>

</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/748816.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx常见的基本配置(全网最详细!!!)

&#x1fa81;Nginx常用命令 &#x1f3a8;Nginx正向代理 &#x1f94f;Nginx反向代理 &#x1f52e;Nginx负载均衡 &#x1f381;Nginx基本配置详解 ## # 全局配置 ##user www-data; ## 配置 worker 进程的用户和组 worker_processes auto; ## 配置 worke…

台球厅助教软件开发台球陪练系统源码助教陪练行业市场分析

市场分析 随着台球运动的普及和竞技水平的不断提升&#xff0c;台球助教市场也逐渐成为了一个备受关注的领域。然而&#xff0c;如何高效、规范地管理这一市场&#xff0c;确保助教质量&#xff0c;提升用户体验&#xff0c;成为了摆在我们面前的一大挑战。幸运的是&#xff0…

网络安全 DVWA通关指南 Cross Site Request Forgery (CSRF)

DVWA Cross Site Request Forgery (CSRF) 文章目录 DVWA Cross Site Request Forgery (CSRF)DVWA Low 级别 CSRFDVWA Medium 级别 CSRFDVWA High 级别 CSRFDVWA Impossible 级别 CSRF CSRF是跨站请求伪造攻击&#xff0c;由客户端发起&#xff0c;是由于没有在执行关键操作时&a…

【数据分析】重仓白酒的资金,流向哪里?

内容提要 更多重仓白酒的资金向红利&#xff08;主要是银行少量的石油石化&#xff09;调仓&#xff0c;有较少部分向通信和电子调仓&#xff08;尤其是消费电子&#xff09;&#xff0c;对出海的调仓目前看并不清晰。 文章正文 近期白酒板块跌幅较大&#xff0c;催化因素在…

轨迹跟踪PID相关及前馈引入结果对比

1、前言&#xff1a; 记录下给定一段轨迹如何实现跟踪控制&#xff0c;主要使用几个经典的方法进行对比。一些讨论的部分在代码中的注释部分。 前馈控制可以在没有扰动和建模误差的理想条件下很好地跟踪期望输出&#xff0c;但在实际系统中&#xff0c;由于存在建模误差、外部扰…

网络安全入门必选:十款免费的抓包工具有哪些?

下面给大家推荐几款好用的免费的抓包工具软件&#xff0c;有需要的小伙伴们来了解一下。 1. Wireshark抓包分析工具 4.0.1 Wireshark是一款功能强大的网络协议分析器&#xff0c;可以实时检测和抓取网络通讯数据。它支持多种协议和媒体类型&#xff0c;并具备丰富的显示过滤…

汇凯金业:如何正确做黄金趋势单

炒黄金过程中&#xff0c;如何准确把握趋势并顺势而为&#xff0c;是大多数投资者面临的普遍问题。专家们经常强调顺势而为的重要性&#xff0c;但具体应如何操作&#xff0c;许多投资者仍然存在疑惑。本文将从五个关键策略入手&#xff0c;为您详细解析黄金趋势单的操作方法&a…

基于Python学生成绩管理系统详细设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

Flutter 小技巧之为什么推荐 Widget 使用 const

今天收到这个问题&#xff0c;本来想着简单回复下&#xff0c;但是感觉这个话题又可以稍微展开讲讲&#xff0c;干脆就整理成一篇简单的科普&#xff0c;这样也能更方便清晰地回答这个问题。 聊这个问题之前&#xff0c;我们需要把一个“老生常谈”的概念拿出来说&#xff0c;那…

安宝特方案 | AR眼镜+手套式扫码枪,智能化仓储创新应用

新扫码设备形态&#xff0c;提升工作效率 传统的仓储工作通常依赖人工操作和纸质记录&#xff0c;这不仅耗时又容易出错。 引入穿戴式AR设备和手套式扫码枪后&#xff0c;AR眼镜可以将数字信息直接叠加在视野中&#xff0c;仓储工人只需佩戴设备即可看到货物位置、库存数量和任…

3. 向索引库中导入数据

1. 准备数据库对象 import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstr…

高校外卖点餐系统

摘 要 随着互联网的快速发展&#xff0c;外卖点餐已经成为人们生活中的一部分。为了满足高校学生的需求&#xff0c;本文设计和实现了一个基于Java Web的高校外卖点餐系统。该系统采用B/S架构&#xff0c;使用Java语言和SSM框架进行开发&#xff0c;前端使用Vue框架进行设计。…

idea中使用springboot进行开发时遇到的工程结构问题汇总

idea中的工程结构和eclipse中不同&#xff0c;但是配置的内容都是一样的。 IDEA中也就是这个页面&#xff0c;快捷键ctrlaltshifts 如果在eclipse中&#xff0c;经常会遇到jre和jdk不正确的情况&#xff0c;但IDEA中这个问题很少&#xff0c;但是IDEA中会经常由于未正常配置根…

基于SpringBoot音乐网站与分享平台详细设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; Java精品实战案例《600套》 2023-2025年最值得选择的Java毕业设计选题大全&#xff1…

浅谈逻辑控制器之仅一次控制器

浅谈逻辑控制器之仅一次控制器 “仅一次控制器”(Once Only Controller) 是一个非常实用的组件&#xff0c;它允许用户控制测试计划中的某些操作仅执行一次&#xff0c;无论其所在的线程组或父级控制器设置了多少次循环。本指南将详细介绍“仅一次控制器”的功能、使用场景及配…

RSA非对称加密-openssl命令及C语言实现

RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。本文介绍如何使用openssl命令和C代码实现基础的RSA加/解密和签名/验签功能。 一、openssl命令实现RSA加解密 1、生成私钥和公钥 生成私钥 openssl genrsa -out private.key 2048 #…

『Z-Workshop』 6月22日线下ALCOVE分享活动

2024 求是创新 ZJUBCA Sponsored by the ALCOVE Community TIME&#xff1a;2024/06/22 ADD&#xff1a;浙江大学紫金港校区 --- Alcove 是 Aptos 公链与 Alibaba Cloud 共同打造的亚洲首个 Move 开发者社区&#xff0c;致力于支持开发者使用 Move 语言构建下一代 Web3 应用&am…

UE4_材质_水涟漪、水深制作_Ben教程

学习笔记&#xff0c;不喜勿喷&#xff01;侵权立删&#xff0c;祝愿生活越来越好&#xff01; 效果图如下&#xff1a; 创建水材质的教程&#xff0c;首先需要外出收集一些参考&#xff0c;看一看你将要做的事情很重要&#xff0c;确定将要模仿物体的关键属性&#xff0c;从这…

Java网络编程(JavaWeb的基础)

Java网络编程&#xff08;JavaWeb的基础&#xff09; 文章目录 Java网络编程&#xff08;JavaWeb的基础&#xff09;前言一、网络编程概述1.1 软件架构&网络基础1.2 网络通信要素:IP/端口/通信协议1.3 传输层协议:tcp/udp 二、网络编程API2.1 InetAddress类2.2 Socket类&am…

第三届人工智能、物联网与云计算技术国际会议(AIoTC 2024)

第三届人工智能、物联网与云计算技术国际会议(AIoTC 2024)将于2024年9月13日-15日在中国武汉举行。本次会议由华中师范大学伍伦贡联合研究院与南京大学联合主办、江苏省大数据区块链与智能信息专委会承办、江苏省概率统计学会、江苏省应用统计学会、Sir Forum、南京理工大学、南…