iOS套接字连接小票打印机

KriThomason 7年前
   <p>最近在开发iOS连接打印机打印小票的功能,找了很多资料,本身相关资料就少,而且还全都是c#,java的.项目是用swift写的(OC思路差不多),现整理如下.</p>    <p>准备工作:Xcode7.1,swift2.0,佳博GP80106 wifi打印机(根据打印机的不同,有些指令可能会有差异,但基本相同,用的都是ESC/POS指令)</p>    <p>前提:在同一个局域网下,设置好打印机,设置好host,port</p>    <h3>一. Socket连接打印机</h3>    <p>使用CocoaAsyncSocket,wifi连接打印机.具体连接方法不是本文主要内容,可参考CocoaAsyncSocket的使用</p>    <p><a href="/misc/goto?guid=4958870675441281877" rel="nofollow,noindex">https://github.com/robbiehanson/CocoaAsyncSocket</a></p>    <p>这个是OC库,所以要将AsyncSocket.h 包含到桥接文件中PrijectName-Bridging-Header.h</p>    <pre>  <code class="language-objectivec">//ViewController.swift  import AsyncSocke  var asyncSocket:AsyncSocket?  //...  //假设已经连接成功</code></pre>    <h3>二. 编写之前,先来看看socket是如何发送指令的:</h3>    <pre>  <code class="language-objectivec">asyncSocket.writeData(data: NSData!, withTimeout: NSTimeInterval, tag: Int)</code></pre>    <p>它是将所有指令数据写入到一个数据对象中,再发送到连接的设备(打印机),那么首先创建一个NSData对象,来储存指令</p>    <pre>  <code class="language-objectivec">let sendData = NSMutableData(capacity: 0)!  asyncSocket.writeData(sendData, withTimeout: -1, tag: 0)</code></pre>    <p>最后将指令发送,因此在这里,关键就是构建sendData对象</p>    <p>先来创建一个通用方法,将每一条指令都写入sendData中(如果现在不知道怎么回事,看下去就明白了)</p>    <pre>  <code class="language-objectivec">func addBytesCommand(command:UnsafePointer, length:Int){        self.sendData.appendBytes(command, length: length)  }</code></pre>    <h3>三. 举几个例子,来演示一下如何发送指令</h3>    <p>根据编程手册,"横向和纵向移动单位"在很多设置间距的地方有使用到,那么先来解决这条指令.</p>    <p>1. GS P x y设置横向和纵向移动单位</p>    <p>[格式]</p>    <table>     <thead>      <tr>       <th>ASCII码</th>       <th>GS</th>       <th>P</th>       <th>x</th>       <th>y</th>      </tr>     </thead>     <tbody>      <tr>       <td>十六进制码</td>       <td>1D</td>       <td>50</td>       <td>x</td>       <td>y</td>      </tr>      <tr>       <td>十进制码</td>       <td>29</td>       <td>80</td>       <td>x</td>       <td>y</td>      </tr>     </tbody>    </table>    <p>[范围] 0≤x≤255 0≤y≤255</p>    <p>[描述] •分别将横向移动单位近似设置成25.4/ x mm(1/ x英寸)纵向移动单位设置成25.4/ y mm(1/ y英寸)。</p>    <ul>     <li>当x和y为0时,x和y被设置成默认值,默认值x=200,y=200。</li>    </ul>    <p>先来解释一下,GS, P就是ASCII码,下面对应的是十六进制和十进制,这里使用十六进制</p>    <pre>  <code class="language-objectivec">func printDotDistance(x:UInt8, y:UInt8){         var char:[UInt8] = [0x1D,0x50]         char.append(x)         char.append(y)        //步骤2中创建的通用数据写入方法         addBytesCommand(char, length: char.count)  }</code></pre>    <p>直接使用指令中参数有时会让你不知所云,当我们将这个接口提供给别人用的时候,他们或许并不知道x,y是什么意思.所以很有必要将参数设置成大家都明白的意思(之后也会看到其他方法),改动如下</p>    <pre>  <code class="language-objectivec">/**  设置横向和纵向移动单位  - parameter horizontal: 横向移动单位  - parameter vertical:  纵向移动单位  */    func printDotDistance(w w:Float, h:Float){         var char = [0x1D,0x50]         char.append(Int(25.4/w))         char.append(Int(25.4/h))         addBytesCommand(char, length: char.count)  }</code></pre>    <p>w,h是我希望设置的左右或上下移动的单位距离,单位mm.根据0≤x≤255,0≤y≤255和"移动单位近似设置成25.4/ x mm" 的规定,上述方法中w,h也是有范围的,不要越界了,不然指令无效,会变成默认设置</p>    <p>下面再来看一条</p>    <p>2.GS L nL nH 设置左边距</p>    <p>[格式]</p>    <table>     <thead>      <tr>       <th>ASCII码</th>       <th>GS</th>       <th>L</th>       <th>nL</th>       <th>nH</th>      </tr>     </thead>     <tbody>      <tr>       <td>十六进制码</td>       <td>1D</td>       <td>4C</td>       <td>nL</td>       <td>nH</td>      </tr>      <tr>       <td>十进制码</td>       <td>29</td>       <td>76</td>       <td>nL</td>       <td>nH</td>      </tr>     </tbody>    </table>    <p>[范围] 0≤nL≤255,0≤nH≤255</p>    <p>[描述] •用nL和nH设置左边距;</p>    <p>•左边距设置为[( nL + nH × 256)×横向移动单位]]英寸。</p>    <p>[默认值] nL = 0, nH = 0</p>    <p>这里用到了刚才设置的"横向移动单位",我先调用以下方法,就将横向纵向移动单位设置成了0.1mm</p>    <pre>  <code class="language-objectivec">printDotDistance(w: 0.1, h: 0.1)</code></pre>    <p>和指令一一样,如果直接将nL,nH设置成参数,使用者不易读,因此参数需要转换下.很容易发现,nL就是模(余数),nH就是商</p>    <pre>  <code class="language-objectivec">/**  设置左边距  - parameter margin: 做边距,单位mm  */  func printLeftMargin(margin:CGFloat){         var char:[UInt8] = [0x1D,0x4C]         char.append(UInt8(margin%256))         char.append(UInt8(margin/256))         addBytesCommand(char, length: char.count)  }</code></pre>    <p>3.文字打印</p>    <pre>  <code class="language-objectivec">/**    添加文字    - parameter text: 字符串    */    func printAddText(text:String){    //文字编码转为GB_18030_2000    let gbkeEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))  let data = text.dataUsingEncoding(gbkeEncoding)!  let size = data.length  let textData = malloc(size)  data.getBytes(textData, length:size)  addBytesCommand(textData, length: size)  free(textData)  }</code></pre>    <p>4.打印图片</p>    <p>我用了其中转图片的方法,使用OC写的,因为项目急,直接用了.等有时间,转成swift,再帖上来.</p>    <p>选择位图模式:ESC * m nL nH d1... dk</p>    <table>     <thead>      <tr>       <th>ASCII码</th>       <th>ESC</th>       <th>*</th>       <th>m</th>       <th>nL</th>       <th>nH</th>       <th>d1...dk</th>      </tr>     </thead>     <tbody>      <tr>       <td>十六进制码</td>       <td>1B</td>       <td>2A</td>       <td>m</td>       <td>nL</td>       <td>nH</td>       <td>d1...dk</td>      </tr>      <tr>       <td>十进制码</td>       <td>27</td>       <td>42</td>       <td>m</td>       <td>nL</td>       <td>nH</td>       <td>d1...dk</td>      </tr>     </tbody>    </table>    <p>[范围] m = 0, 1, 32, 33,0≤nL≤255,0≤nH≤3,0≤d≤255</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6f4894fb7eae63b2b3f9464eccdfbb54.png"></p>    <pre>  <code class="language-objectivec">/**  选择位图模式  - parameter bitmap:    位图  */    func printBitmapModel(bitmap:UIImage){  let data = IGThermalSupport.imageToThermalData(bitmap)//ThermalPrinterKit这个库中的方法  let size = data.length  let picData = malloc(size)  data.getBytes(picData, length:size)  addBytesCommand(picData, length: size)  free(picData)  }</code></pre>    <p>打印二维码,可以先生成二维码图片,再使用上面的方法写入数据.</p>    <p>5.打印小票</p>    <p>最后别忘了使用</p>    <pre>  <code class="language-objectivec">asyncSocket.writeData(sendData, withTimeout: -1, tag: 0)</code></pre>    <p>将数据发送给打印机</p>    <h3>四.注意点</h3>    <ol>     <li>为了方便使用,可以将socket连接、打印机管理方法分别封装,再封装一个小票管理类直接使用.</li>     <li>ESC/POS指令中所有参数都是UInt8类型的,有时候可能是参数类型错误,将类型强转为UInt8(para)试试,可能就成功了</li>     <li>第一次开发打印机,如果有错误,欢迎指正交流.</li>    </ol>    <h3> </h3>    <p> </p>    <p>来自:http://www.jianshu.com/p/52bdd2e41b11</p>    <p> </p>