Explorar el Código

adding printer testing

master
[email protected] hace 3 semanas
padre
commit
40fcaba4b0
Se han modificado 3 ficheros con 333 adiciones y 0 borrados
  1. +221
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/PlasticBagPrinterService.kt
  2. +90
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/PlasticBagPrinterController.kt
  3. +22
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/PlasticPrintRequest.kt

+ 221
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/service/PlasticBagPrinterService.kt Ver fichero

@@ -0,0 +1,221 @@
package com.ffii.fpsms.modules.jobOrder.service

import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository
import com.ffii.fpsms.modules.jobOrder.web.model.PrintRequest
import com.ffii.fpsms.modules.jobOrder.web.model.LaserRequest
import org.springframework.stereotype.Service

import java.awt.Color
import java.awt.Font
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import javax.imageio.ImageIO
import com.google.zxing.BarcodeFormat
import com.google.zxing.qrcode.QRCodeWriter
import java.net.Socket
import java.net.InetSocketAddress
import java.io.PrintWriter
import java.io.DataOutputStream
import java.nio.charset.Charset

@Service
open class PlasticBagPrinterService(
val jobOrderRepository: JobOrderRepository,
) {
fun generatePrintJobBundle(itemCode: String, lotNo: String, expiryDate: String, productName: String): ByteArray {
val baos = ByteArrayOutputStream()
ZipOutputStream(baos).use { zos ->
// Use unique names based on the Lot Number
val nameFile = "${lotNo}_product.bmp"
val expFile = "${lotNo}_expiry.bmp"
val qrFile = "${lotNo}_qr.bmp"

// 1. Generate Bitmaps
addToZip(zos, nameFile, createMonochromeBitmap(productName, 5365, 704))
addToZip(zos, expFile, createMonochromeBitmap(expiryDate, 4203, 1173))
addToZip(zos, qrFile, createQrCodeBitmap("$itemCode|$lotNo|$expiryDate", 1000))

// 2. Generate the .image file with dynamic references
val imageXml = """
<Legend type="Image.V1" PaperColor="White" BackgroundColor="DarkGray">
<FieldList>
<Logo>
<Name>PRODUCT_NAME</Name>
<Geometry><X>0</X><Y>250</Y></Geometry>
<FileName>$nameFile</FileName>
</Logo>
<Logo>
<Name>EXPIRY_DATE</Name>
<Geometry><X>500</X><Y>2500</Y></Geometry>
<FileName>$expFile</FileName>
</Logo>
<Logo>
<Name>QR_CODE</Name>
<Geometry><X>750</X><Y>3750</Y></Geometry>
<FileName>$qrFile</FileName>
</Logo>
</FieldList>
</Legend>
""".trimIndent()
addToZip(zos, "$lotNo.image", imageXml.toByteArray())
// 3. Generate the .job file pointing to the new .image [cite: 2]
val jobXml = "<Job><ImageFileName>$lotNo.image</ImageFileName></Job>"
addToZip(zos, "$lotNo.job", jobXml.toByteArray())
}
return baos.toByteArray()
}

private fun createMonochromeBitmap(text: String, width: Int, height: Int): ByteArray {
val image = BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY) // Essential for printers
val g = image.createGraphics()
g.color = Color.WHITE
g.fillRect(0, 0, width, height)
g.color = Color.BLACK
g.font = Font("Arial", Font.BOLD, 400)
g.drawString(text, 50, height - 200)
g.dispose()

val baos = ByteArrayOutputStream()
ImageIO.write(image, "bmp", baos)
return baos.toByteArray()
}

private fun createQrCodeBitmap(content: String, size: Int): ByteArray {
val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, size, size)
val image = BufferedImage(size, size, BufferedImage.TYPE_BYTE_BINARY)
for (x in 0 until size) {
for (y in 0 until size) {
image.setRGB(x, y, if (bitMatrix.get(x, y)) Color.BLACK.rgb else Color.WHITE.rgb)
}
}
val baos = ByteArrayOutputStream()
ImageIO.write(image, "bmp", baos)
return baos.toByteArray()
}

private fun addToZip(zos: ZipOutputStream, fileName: String, content: ByteArray) {
zos.putNextEntry(ZipEntry(fileName))
zos.write(content)
zos.closeEntry()
}
fun sendDataFlexJob(request: PrintRequest) {
try {
// 1. Establish Socket Connection
val socket = Socket(request.printerIp, request.printerPort)
socket.soTimeout = 5000 // 5 seconds timeout
val writer = PrintWriter(socket.getOutputStream(), true)

// 2. Format the command for DataFlex 6330
// Note: This format depends on your specific label design in CLARiSOFT.
// Many DataFlex printers use the "Job Select" or "Set Variable" protocol.
val command = """
^SVAR1|${request.itemCode}
^SVAR2|${request.itemName}
^SVAR3|${request.lotNo}
^SVAR4|${request.expiryDate}
^PRNT1
""".trimIndent()

// 3. Send and Close
writer.print(command)
writer.flush()
socket.close()
println("Successfully sent command to DataFlex at ${request.printerIp}")
} catch (e: Exception) {
throw RuntimeException("Failed to communicate with DataFlex printer: ${e.message}")
}
}

fun sendLaserMark(request: LaserRequest) {
Socket().use { socket ->
socket.connect(InetSocketAddress(request.printerIp, request.printerPort.toInt()), 3000)
val writer = PrintWriter(socket.getOutputStream(), true)

// Standard HANS/General Laser Command Format:
// [STX]Command|Variable1|Variable2[ETX]
// Note: Exact protocol depends on your HANS controller software (usually JCZ or similar)
val command = "STRSET|${request.templateId}|LOT=${request.lotNo}|EXP=${request.expiryDate}\n"
writer.print(command)
writer.flush()
// Optional: Trigger the marking immediately
// writer.print("STRMARK\n")
// writer.flush()
}
}

/*
fun previewLaser(request: LaserRequest) {
Socket().use { socket ->
socket.connect(InetSocketAddress(request.printerIp, request.printerPort), 2000)
val writer = PrintWriter(socket.getOutputStream(), true)
// Typical HANS command for Red Light Preview
writer.println("JOBLOAD|${request.templateId}")
writer.println("REDLIGHT|1") // 1 to turn on, 0 to turn off
writer.flush()
}
} */

fun sendLaserPreview(request: LaserRequest) {
Socket().use { socket ->
socket.connect(InetSocketAddress(request.printerIp, request.printerPort), 3000)
val writer = PrintWriter(socket.getOutputStream(), true)

// HANS Protocol for Red Light
// Often requires loading the job first so it knows the boundary
val command = """
JOBLOAD|${request.templateId}
REDLIGHT|1
""".trimIndent()

writer.println(command)
writer.flush()
}
}

fun sendTscPrintJob(request: PrintRequest) {
Socket().use { socket ->
try {
socket.connect(InetSocketAddress(request.printerIp, request.printerPort), 3000)
val out = DataOutputStream(socket.getOutputStream())

// Construct TSPL commands
// Note: Coordinates (x,y) are in dots.
// For 203 DPI: 8 dots = 1mm.
val tspl = StringBuilder()
.append("SIZE 100 mm, 50 mm\n") // Adjust to your label size
.append("GAP 3 mm, 0 mm\n")
.append("DIRECTION 1\n")
.append("CLS\n") // Clear buffer
// Text commands: TEXT x, y, "font", rotation, x-multi, y-multi, "content"
.append("TEXT 50,50,\"ROMAN.TTF\",0,1,1,\"ITEM: ${request.itemCode}\"\n")
.append("TEXT 50,100,\"ROMAN.TTF\",0,1,1,\"NAME: ${request.itemName}\"\n")
.append("TEXT 50,150,\"ROMAN.TTF\",0,1,1,\"LOT: ${request.lotNo}\"\n")
.append("TEXT 50,200,\"ROMAN.TTF\",0,1,1,\"EXP: ${request.expiryDate}\"\n")
.append("PRINT 1,1\n")
.toString()

// TSC printers usually expect encoding in Windows-1252 or Thai (TIS-620)
val bytes = tspl.toByteArray(Charset.forName("TIS-620"))
out.write(bytes)
out.flush()
} catch (e: Exception) {
throw RuntimeException("TSC Printer Error: ${e.message}")
}
}
}

}

+ 90
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/web/PlasticBagPrinterController.kt Ver fichero

@@ -0,0 +1,90 @@
package com.ffii.fpsms.modules.jobOrder.web

import com.ffii.fpsms.modules.jobOrder.service.PlasticBagPrinterService
import com.ffii.fpsms.modules.jobOrder.web.model.PrintRequest
import com.ffii.fpsms.modules.jobOrder.web.model.LaserRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.http.HttpHeaders
import org.springframework.web.bind.annotation.*
import java.time.LocalDate
import org.springframework.http.ResponseEntity

@RestController
@RequestMapping("/plastic")
class PlasticBagPrinterController(
private val plasticBagPrinterService: PlasticBagPrinterService,
) {

/**
* Test API to generate and download the printer job files as a ZIP.
* ONPACK2030
*/
@GetMapping("/get-printer6")
fun testPrintJob(
@RequestParam itemCode: String,
@RequestParam lotNo: String,
@RequestParam expiryDate: String,
@RequestParam productName: String,
response: HttpServletResponse
) {
// Generate the ZIP bundle via the service
val zipBytes = plasticBagPrinterService.generatePrintJobBundle(
itemCode,
lotNo,
expiryDate,
productName
)

// Set headers for file download
response.contentType = "application/zip"
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"Job_$lotNo.zip\"")
response.setContentLength(zipBytes.size)

// Write to response stream
response.outputStream.write(zipBytes)
response.outputStream.flush()
}

@PostMapping("/print-dataflex")
fun printDataFlex(@RequestBody request: PrintRequest): ResponseEntity<String> {
return try {
plasticBagPrinterService.sendDataFlexJob(request)
ResponseEntity.ok("Print command sent successfully")
} catch (e: Exception) {
ResponseEntity.status(500).body("Error: ${e.message}")
}
}

@PostMapping("/print-laser")
fun printLaser(@RequestBody request: LaserRequest): ResponseEntity<String> {
return try {
// Call the laser marking service
plasticBagPrinterService.sendLaserMark(request)
ResponseEntity.ok("Laser marking command sent successfully to ${request.printerIp}")
} catch (e: Exception) {
// Log the error and return 500 status
ResponseEntity.status(500).body("Laser Error: ${e.message}")
}
}

@PostMapping("/preview-laser")
fun previewLaser(@RequestBody request: LaserRequest): ResponseEntity<String> {
return try {
plasticBagPrinterService.sendLaserPreview(request)
ResponseEntity.ok("Red light activated")
} catch (e: Exception) {
ResponseEntity.status(500).body("Preview Error: ${e.message}")
}
}

@PostMapping("/print-tsc")
fun printTsc(@RequestBody request: PrintRequest): ResponseEntity<String> {
return try {
plasticBagPrinterService.sendTscPrintJob(request)
ResponseEntity.ok("TSC Print command sent to ${request.printerIp}")
} catch (e: Exception) {
ResponseEntity.status(500).body("Error: ${e.message}")
}
}

}

+ 22
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/PlasticPrintRequest.kt Ver fichero

@@ -0,0 +1,22 @@
package com.ffii.fpsms.modules.jobOrder.web.model

data class PrintRequest(
val itemCode: String,
val itemName: String,
val lotNo: String,
val expiryDate: String,
val printerIp: String,
val printerPort: Int
)

data class LaserRequest(
val templateId: String, // The name of the file on the laser controller
val itemCode: String,
val itemName: String,
val lotNo: String,
val expiryDate: String,
val power: String, // Laser intensity (0-100)
val speed: String, // Marking speed
val printerIp: String,
val printerPort: Int
)

Cargando…
Cancelar
Guardar