updated : solutions
This commit is contained in:
@@ -1,74 +0,0 @@
|
|||||||
# Windows Print Service Network Configuration Guide
|
|
||||||
|
|
||||||
## Current Issue
|
|
||||||
Your Flask server (Linux) cannot connect to Windows Print Service because they're on different machines.
|
|
||||||
|
|
||||||
## Solution Options
|
|
||||||
|
|
||||||
### Option 1: Same Machine Setup
|
|
||||||
If both Flask and Windows service are on the same machine:
|
|
||||||
```python
|
|
||||||
WINDOWS_PRINT_SERVICE_URL = "http://localhost:8765"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Different Machines (Recommended)
|
|
||||||
If Flask server (Linux) and Windows service are on different machines:
|
|
||||||
|
|
||||||
1. **Find Windows Machine IP Address:**
|
|
||||||
```cmd
|
|
||||||
# On Windows machine, run:
|
|
||||||
ipconfig
|
|
||||||
# Look for IPv4 Address, e.g., 192.168.1.100
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Update Configuration:**
|
|
||||||
```python
|
|
||||||
# In /app/print_config.py, change:
|
|
||||||
WINDOWS_PRINT_SERVICE_URL = "http://192.168.1.100:8765"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Test Connection:**
|
|
||||||
```bash
|
|
||||||
# From Linux machine, test:
|
|
||||||
curl http://192.168.1.100:8765/health
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 3: Windows Firewall Configuration
|
|
||||||
Ensure Windows allows incoming connections on port 8765:
|
|
||||||
|
|
||||||
1. **Windows Firewall Settings:**
|
|
||||||
- Control Panel → System and Security → Windows Defender Firewall
|
|
||||||
- Advanced Settings → Inbound Rules → New Rule
|
|
||||||
- Port → TCP → 8765 → Allow the connection
|
|
||||||
|
|
||||||
2. **Test from Linux:**
|
|
||||||
```bash
|
|
||||||
telnet [WINDOWS_IP] 8765
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 4: Port Forwarding/Tunneling
|
|
||||||
If direct connection isn't possible, set up SSH tunnel or port forwarding.
|
|
||||||
|
|
||||||
## Quick Fix for Testing
|
|
||||||
|
|
||||||
1. **Get Windows IP Address**
|
|
||||||
2. **Update print_config.py with the IP**
|
|
||||||
3. **Restart Flask server**
|
|
||||||
4. **Test printing**
|
|
||||||
|
|
||||||
## Current Status
|
|
||||||
- ✅ Windows Print Service: Running (localhost:8765/health works)
|
|
||||||
- ❌ Network Connection: Flask can't reach Windows service
|
|
||||||
- 🔧 Fix Needed: Update WINDOWS_PRINT_SERVICE_URL with correct IP
|
|
||||||
|
|
||||||
## Example Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Find Windows IP (run on Windows):
|
|
||||||
# ipconfig | findstr IPv4
|
|
||||||
|
|
||||||
# 2. Test connection (run on Linux):
|
|
||||||
# curl http://[WINDOWS_IP]:8765/health
|
|
||||||
|
|
||||||
# 3. Update config and restart Flask
|
|
||||||
```
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
PDF Label Dimension Analysis Tool
|
|
||||||
Analyzes the generated PDF to verify exact dimensions and content positioning
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
|
|
||||||
|
|
||||||
from app.pdf_generator import LabelPDFGenerator, mm_to_points
|
|
||||||
from reportlab.lib.units import mm
|
|
||||||
|
|
||||||
def analyze_pdf_dimensions():
|
|
||||||
"""Analyze the PDF dimensions in detail"""
|
|
||||||
print("=== PDF Label Dimension Analysis ===\n")
|
|
||||||
|
|
||||||
generator = LabelPDFGenerator()
|
|
||||||
|
|
||||||
# Label dimensions
|
|
||||||
print("1. LABEL PHYSICAL DIMENSIONS:")
|
|
||||||
print(f" Width: {generator.label_width/mm:.1f}mm ({generator.label_width:.2f} points)")
|
|
||||||
print(f" Height: {generator.label_height/mm:.1f}mm ({generator.label_height:.2f} points)")
|
|
||||||
print(f" ✓ Should be exactly 80.0mm × 110.0mm")
|
|
||||||
|
|
||||||
# Content area
|
|
||||||
print(f"\n2. CONTENT AREA DIMENSIONS:")
|
|
||||||
print(f" Content Width: {generator.content_width/mm:.1f}mm")
|
|
||||||
print(f" Content Height: {generator.content_height/mm:.1f}mm")
|
|
||||||
print(f" Left margin: {generator.content_x/mm:.1f}mm")
|
|
||||||
print(f" Bottom margin: {generator.content_y/mm:.1f}mm")
|
|
||||||
print(f" Right margin: {(generator.label_width - generator.content_x - generator.content_width)/mm:.1f}mm")
|
|
||||||
print(f" Top margin: {(generator.label_height - generator.content_y - generator.content_height)/mm:.1f}mm")
|
|
||||||
|
|
||||||
# Row analysis
|
|
||||||
print(f"\n3. ROW LAYOUT:")
|
|
||||||
total_rows = 9
|
|
||||||
standard_rows = 8 # 7 standard + 1 double
|
|
||||||
double_rows = 1
|
|
||||||
|
|
||||||
standard_row_height_mm = generator.row_height/mm
|
|
||||||
double_row_height_mm = generator.double_row_height/mm
|
|
||||||
|
|
||||||
total_content_used = (standard_rows * standard_row_height_mm + double_rows * double_row_height_mm)
|
|
||||||
expected_content_height = generator.content_height/mm
|
|
||||||
|
|
||||||
print(f" Standard row height: {standard_row_height_mm:.1f}mm")
|
|
||||||
print(f" Double row height: {double_row_height_mm:.1f}mm")
|
|
||||||
print(f" Total rows: {total_rows} (8 standard + 1 double)")
|
|
||||||
print(f" Content height used: {total_content_used:.1f}mm")
|
|
||||||
print(f" Content area available: {expected_content_height:.1f}mm")
|
|
||||||
print(f" Remaining space: {expected_content_height - total_content_used:.1f}mm")
|
|
||||||
|
|
||||||
# Barcode area
|
|
||||||
print(f"\n4. BARCODE AREA:")
|
|
||||||
barcode_bottom_margin = generator.content_y/mm
|
|
||||||
print(f" Bottom barcode space: {barcode_bottom_margin:.1f}mm (should be ~12-15mm)")
|
|
||||||
|
|
||||||
top_margin = (generator.label_height - generator.content_y - generator.content_height)/mm
|
|
||||||
print(f" Top margin space: {top_margin:.1f}mm")
|
|
||||||
|
|
||||||
# Check for potential issues
|
|
||||||
print(f"\n5. POTENTIAL ISSUES ANALYSIS:")
|
|
||||||
|
|
||||||
# Check if content is too high
|
|
||||||
if total_content_used > expected_content_height:
|
|
||||||
print(f" ⚠️ ISSUE: Content ({total_content_used:.1f}mm) exceeds available space ({expected_content_height:.1f}mm)")
|
|
||||||
else:
|
|
||||||
print(f" ✓ Content fits within available space")
|
|
||||||
|
|
||||||
# Check margins
|
|
||||||
if barcode_bottom_margin < 10:
|
|
||||||
print(f" ⚠️ WARNING: Bottom margin ({barcode_bottom_margin:.1f}mm) may be too small for barcode")
|
|
||||||
else:
|
|
||||||
print(f" ✓ Bottom margin adequate for barcode")
|
|
||||||
|
|
||||||
if top_margin < 3:
|
|
||||||
print(f" ⚠️ WARNING: Top margin ({top_margin:.1f}mm) very small")
|
|
||||||
else:
|
|
||||||
print(f" ✓ Top margin adequate")
|
|
||||||
|
|
||||||
# Aspect ratio check
|
|
||||||
aspect_ratio = generator.label_height / generator.label_width
|
|
||||||
expected_ratio = 110.0 / 80.0 # 1.375
|
|
||||||
|
|
||||||
print(f"\n6. ASPECT RATIO CHECK:")
|
|
||||||
print(f" Current ratio: {aspect_ratio:.3f}")
|
|
||||||
print(f" Expected ratio: {expected_ratio:.3f} (110/80)")
|
|
||||||
|
|
||||||
if abs(aspect_ratio - expected_ratio) < 0.001:
|
|
||||||
print(f" ✓ Aspect ratio is correct")
|
|
||||||
else:
|
|
||||||
print(f" ⚠️ Aspect ratio mismatch!")
|
|
||||||
|
|
||||||
# Browser display notes
|
|
||||||
print(f"\n7. BROWSER DISPLAY CONSIDERATIONS:")
|
|
||||||
print(f" • Browser zoom affects apparent size")
|
|
||||||
print(f" • PDF viewer scaling can change appearance")
|
|
||||||
print(f" • Monitor DPI affects screen dimensions")
|
|
||||||
print(f" • Print preview scaling may alter appearance")
|
|
||||||
print(f"\n 🔧 SOLUTION: Always verify with physical printout at 100% scale")
|
|
||||||
|
|
||||||
return {
|
|
||||||
'width_mm': generator.label_width/mm,
|
|
||||||
'height_mm': generator.label_height/mm,
|
|
||||||
'content_height_mm': expected_content_height,
|
|
||||||
'content_used_mm': total_content_used,
|
|
||||||
'aspect_ratio': aspect_ratio,
|
|
||||||
'expected_ratio': expected_ratio
|
|
||||||
}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
results = analyze_pdf_dimensions()
|
|
||||||
|
|
||||||
print(f"\n=== SUMMARY ===")
|
|
||||||
if (abs(results['width_mm'] - 80.0) < 0.1 and
|
|
||||||
abs(results['height_mm'] - 110.0) < 0.1 and
|
|
||||||
abs(results['aspect_ratio'] - results['expected_ratio']) < 0.001):
|
|
||||||
print("✅ PDF dimensions are CORRECT: 80mm × 110mm")
|
|
||||||
print("If labels appear too large, check:")
|
|
||||||
print(" • Browser zoom level (should be 100%)")
|
|
||||||
print(" • PDF viewer scaling settings")
|
|
||||||
print(" • Print scaling (should be 'Actual size' or 100%)")
|
|
||||||
else:
|
|
||||||
print("❌ PDF dimensions need adjustment")
|
|
||||||
Binary file not shown.
@@ -2065,6 +2065,47 @@ def update_printed_status(order_id):
|
|||||||
print(f"DEBUG: Error in update_printed_status: {e}")
|
print(f"DEBUG: Error in update_printed_status: {e}")
|
||||||
return jsonify({'error': 'Internal server error'}), 500
|
return jsonify({'error': 'Internal server error'}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/download_print_service')
|
||||||
|
def download_print_service():
|
||||||
|
"""Download the direct print service package"""
|
||||||
|
try:
|
||||||
|
from flask import send_from_directory, current_app
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Check if complete package is requested
|
||||||
|
package_type = request.args.get('package', 'basic')
|
||||||
|
|
||||||
|
if package_type == 'complete':
|
||||||
|
# Serve the complete package
|
||||||
|
filename = 'QualityPrintService_Complete.zip'
|
||||||
|
else:
|
||||||
|
# Serve the basic package (legacy)
|
||||||
|
filename = 'RecticelPrintService.zip'
|
||||||
|
|
||||||
|
# Path to the print service files
|
||||||
|
service_path = os.path.join(current_app.root_path, 'static', 'downloads')
|
||||||
|
|
||||||
|
# Create the directory if it doesn't exist
|
||||||
|
os.makedirs(service_path, exist_ok=True)
|
||||||
|
|
||||||
|
# Check if the zip file exists
|
||||||
|
full_path = os.path.join(service_path, filename)
|
||||||
|
|
||||||
|
if not os.path.exists(full_path):
|
||||||
|
# If the zip doesn't exist, return information about creating it
|
||||||
|
return jsonify({
|
||||||
|
'error': f'Print service package ({filename}) not found',
|
||||||
|
'message': f'The {package_type} print service package is not available',
|
||||||
|
'suggestion': 'Please contact the administrator or use the basic package'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
return send_from_directory(service_path, filename, as_attachment=True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"DEBUG: Error downloading print service: {e}")
|
||||||
|
return jsonify({'error': 'Failed to download print service'}), 500
|
||||||
|
|
||||||
@bp.route('/get_order_data/<int:order_id>', methods=['GET'])
|
@bp.route('/get_order_data/<int:order_id>', methods=['GET'])
|
||||||
def get_order_data(order_id):
|
def get_order_data(order_id):
|
||||||
"""Get specific order data for preview"""
|
"""Get specific order data for preview"""
|
||||||
|
|||||||
@@ -204,6 +204,25 @@
|
|||||||
<div style="margin-bottom: 5px;">Creates sequential labels based on quantity</div>
|
<div style="margin-bottom: 5px;">Creates sequential labels based on quantity</div>
|
||||||
<small>(e.g., CP00000711-001 to CP00000711-063)</small>
|
<small>(e.g., CP00000711-001 to CP00000711-063)</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Direct Print Service Download -->
|
||||||
|
<div style="width: 100%; text-align: center; margin-top: 20px; padding-top: 15px; border-top: 1px solid #e9ecef;">
|
||||||
|
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 12px; margin-bottom: 10px;">
|
||||||
|
<div style="font-size: 11px; color: #856404; margin-bottom: 8px;">
|
||||||
|
<strong>📦 Quality Print Service</strong>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 10px; color: #6c757d; margin-bottom: 10px; line-height: 1.3;">
|
||||||
|
Professional Windows service for instant direct printing to thermal label printers without PDF dialogs
|
||||||
|
</div>
|
||||||
|
<a href="{{ url_for('main.download_print_service') }}?package=complete" class="btn btn-outline-warning btn-sm" download style="font-size: 10px; padding: 4px 12px; text-decoration: none;">
|
||||||
|
📥 Download Complete Package
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 9px; color: #6c757d; margin-top: 5px; line-height: 1.2;">
|
||||||
|
<strong>Easy Install:</strong> Extract → Run install.bat as Admin → Done!<br>
|
||||||
|
<small>Supports all thermal printers • Auto-starts with Windows • One-click installation</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- Data Preview Card -->
|
</div> <!-- Data Preview Card -->
|
||||||
<div class="card scan-table-card" style="min-height: 700px; width: calc(100% - 350px); margin: 0;">
|
<div class="card scan-table-card" style="min-height: 700px; width: calc(100% - 350px); margin: 0;">
|
||||||
@@ -330,6 +349,9 @@ document.getElementById('check-db-btn').addEventListener('click', function() {
|
|||||||
// Initialize PDF generation functionality
|
// Initialize PDF generation functionality
|
||||||
addPDFGenerationHandler();
|
addPDFGenerationHandler();
|
||||||
|
|
||||||
|
// Initialize print service
|
||||||
|
initializePrintService();
|
||||||
|
|
||||||
// Auto-select first row
|
// Auto-select first row
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const firstRow = document.querySelector('.print-module-table tbody tr');
|
const firstRow = document.querySelector('.print-module-table tbody tr');
|
||||||
@@ -396,6 +418,96 @@ function updateLabelPreview(order) {
|
|||||||
document.getElementById('barcode-text').textContent = prodOrder;
|
document.getElementById('barcode-text').textContent = prodOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize print service connection and load available printers
|
||||||
|
function initializePrintService() {
|
||||||
|
// Check service status and load printers
|
||||||
|
checkPrintServiceStatus()
|
||||||
|
.then(serviceStatus => {
|
||||||
|
if (serviceStatus) {
|
||||||
|
console.log('✅ Print service is available');
|
||||||
|
loadAvailablePrinters();
|
||||||
|
updateServiceStatusIndicator(true);
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ Print service not available');
|
||||||
|
updateServiceStatusIndicator(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('❌ Print service connection failed:', error);
|
||||||
|
updateServiceStatusIndicator(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load available printers from print service
|
||||||
|
function loadAvailablePrinters() {
|
||||||
|
fetch('http://localhost:8899/printers', {
|
||||||
|
method: 'GET',
|
||||||
|
mode: 'cors'
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const printerSelect = document.getElementById('printerSelect');
|
||||||
|
|
||||||
|
// Clear existing options except default ones
|
||||||
|
const defaultOptions = ['default', 'Epson TM-T20', 'Citizen CTS-310', 'custom'];
|
||||||
|
Array.from(printerSelect.options).forEach(option => {
|
||||||
|
if (!defaultOptions.includes(option.value)) {
|
||||||
|
option.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add available printers
|
||||||
|
if (data.printers && data.printers.length > 0) {
|
||||||
|
data.printers.forEach(printer => {
|
||||||
|
// Don't add if already exists in default options
|
||||||
|
if (!defaultOptions.includes(printer.name)) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = printer.name;
|
||||||
|
option.textContent = `${printer.name}${printer.default ? ' (Default)' : ''}${printer.status !== 'ready' ? ' - Offline' : ''}`;
|
||||||
|
|
||||||
|
// Insert before "Other Printer..." option
|
||||||
|
const customOption = printerSelect.querySelector('option[value="custom"]');
|
||||||
|
printerSelect.insertBefore(option, customOption);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set default printer if available
|
||||||
|
const defaultPrinter = data.printers.find(p => p.default);
|
||||||
|
if (defaultPrinter) {
|
||||||
|
printerSelect.value = defaultPrinter.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Loaded ${data.printers.length} printers from service`);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('Could not load printers from service:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update service status indicator
|
||||||
|
function updateServiceStatusIndicator(isAvailable) {
|
||||||
|
// Find or create status indicator
|
||||||
|
let statusIndicator = document.getElementById('service-status-indicator');
|
||||||
|
if (!statusIndicator) {
|
||||||
|
statusIndicator = document.createElement('div');
|
||||||
|
statusIndicator.id = 'service-status-indicator';
|
||||||
|
statusIndicator.style.cssText = 'font-size: 9px; color: #6c757d; margin-top: 5px; text-align: center;';
|
||||||
|
|
||||||
|
// Insert after printer selection
|
||||||
|
const printerSelection = document.getElementById('printerSelection');
|
||||||
|
printerSelection.appendChild(statusIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAvailable) {
|
||||||
|
statusIndicator.innerHTML = '🟢 <strong>Print service connected</strong><br><small>Direct printing available</small>';
|
||||||
|
statusIndicator.style.color = '#28a745';
|
||||||
|
} else {
|
||||||
|
statusIndicator.innerHTML = '🔴 <strong>Print service offline</strong><br><small>Install and start the service for direct printing</small>';
|
||||||
|
statusIndicator.style.color = '#dc3545';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PDF Generation System - No printer setup needed
|
// PDF Generation System - No printer setup needed
|
||||||
// Labels are generated as PDF files for universal compatibility
|
// Labels are generated as PDF files for universal compatibility
|
||||||
function addPDFGenerationHandler() {
|
function addPDFGenerationHandler() {
|
||||||
@@ -463,67 +575,114 @@ function handleDirectPrint(selectedRow) {
|
|||||||
printerName = prompt('Enter your printer name:');
|
printerName = prompt('Enter your printer name:');
|
||||||
if (!printerName) return;
|
if (!printerName) return;
|
||||||
} else if (printerName === 'default') {
|
} else if (printerName === 'default') {
|
||||||
printerName = 'default';
|
printerName = ''; // Empty for default printer
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Direct printing to: ${printerName}`);
|
console.log(`Direct printing to: ${printerName || 'default printer'}`);
|
||||||
|
|
||||||
// Extract order data from selected row
|
// Check if print service is available
|
||||||
const cells = selectedRow.querySelectorAll('td');
|
checkPrintServiceStatus()
|
||||||
const orderData = {
|
.then(serviceStatus => {
|
||||||
order_id: parseInt(orderId),
|
if (!serviceStatus) {
|
||||||
comanda_productie: cells[1].textContent.trim(),
|
throw new Error('Print service is not available');
|
||||||
cod_articol: cells[2].textContent.trim(),
|
}
|
||||||
descr_com_prod: cells[3].textContent.trim(),
|
|
||||||
cantitate: parseInt(cells[4].textContent.trim()),
|
// Generate PDF first for printing
|
||||||
data_livrare: cells[5].textContent.trim(),
|
return generatePrintablePDF(orderId, true); // true for paper-saving mode
|
||||||
dimensiune: cells[6].textContent.trim(),
|
})
|
||||||
com_achiz_client: cells[7].textContent.trim(),
|
.then(pdfUrl => {
|
||||||
nr_linie_com_client: cells[8].textContent.trim(),
|
// Send PDF to print service
|
||||||
customer_name: cells[9].textContent.trim(),
|
const printData = {
|
||||||
customer_article_number: cells[10].textContent.trim()
|
pdf_url: pdfUrl,
|
||||||
};
|
printer: printerName,
|
||||||
|
copies: 1,
|
||||||
// Print each label individually
|
order_id: parseInt(orderId),
|
||||||
for (let i = 1; i <= quantity; i++) {
|
paper_saving: true
|
||||||
const labelData = {
|
};
|
||||||
...orderData,
|
|
||||||
sequential_number: `${orderData.comanda_productie}-${i.toString().padStart(3, '0')}`,
|
return fetch('http://localhost:8899/print-pdf', {
|
||||||
current_label: i,
|
method: 'POST',
|
||||||
total_labels: quantity
|
headers: {
|
||||||
};
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
// Encode data as base64 JSON
|
body: JSON.stringify(printData)
|
||||||
const jsonString = JSON.stringify(labelData);
|
});
|
||||||
const base64Data = btoa(unescape(encodeURIComponent(jsonString)));
|
})
|
||||||
|
.then(response => {
|
||||||
// Create custom protocol URL
|
if (!response.ok) {
|
||||||
const printUrl = `recticel-print://${encodeURIComponent(printerName)}/${base64Data}`;
|
throw new Error(`Print service error: ${response.status}`);
|
||||||
|
}
|
||||||
console.log(`Printing label ${i}/${quantity}: ${printUrl.substring(0, 100)}...`);
|
return response.json();
|
||||||
|
})
|
||||||
// Trigger direct print via custom protocol
|
.then(result => {
|
||||||
try {
|
if (result.success) {
|
||||||
window.location.href = printUrl;
|
alert(`✅ Successfully sent ${quantity} labels to printer!\n📊 Order: ${prodOrder}\n🖨️ Printer: ${printerName || 'Default'}\n📄 Job ID: ${result.job_id || 'N/A'}`);
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to print label ${i}:`, error);
|
// Update database status and refresh table
|
||||||
alert(`❌ Failed to print label ${i}/${quantity}. Please check if the print service is installed.`);
|
updatePrintedStatus(orderId);
|
||||||
return;
|
} else {
|
||||||
|
throw new Error(result.error || 'Unknown print error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Direct print error:', error);
|
||||||
|
|
||||||
|
let errorMessage = '❌ Direct printing failed: ' + error.message;
|
||||||
|
|
||||||
|
if (error.message.includes('service is not available')) {
|
||||||
|
errorMessage += '\n\n💡 Solutions:\n' +
|
||||||
|
'1. Install the print service using the download link below\n' +
|
||||||
|
'2. Ensure the service is running (check Windows Services)\n' +
|
||||||
|
'3. Try restarting the print service\n' +
|
||||||
|
'4. Use PDF generation as alternative';
|
||||||
|
} else if (error.message.includes('fetch')) {
|
||||||
|
errorMessage += '\n\n💡 The print service may not be running.\n' +
|
||||||
|
'Please start the Quality Print Service from Windows Services.';
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check print service status
|
||||||
|
function checkPrintServiceStatus() {
|
||||||
|
return fetch('http://localhost:8899/status', {
|
||||||
|
method: 'GET',
|
||||||
|
mode: 'cors'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json().then(data => {
|
||||||
|
console.log('Print service status:', data);
|
||||||
|
return data.status === 'running';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Small delay between labels to prevent overwhelming the printer
|
})
|
||||||
if (i < quantity) {
|
.catch(error => {
|
||||||
setTimeout(() => {}, 100);
|
console.log('Print service not available:', error.message);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to generate PDF for printing
|
||||||
|
function generatePrintablePDF(orderId, paperSaving = true) {
|
||||||
|
return fetch(`/generate_labels_pdf/${orderId}/${paperSaving ? 'true' : 'false'}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
.then(response => {
|
||||||
// Show success message
|
if (!response.ok) {
|
||||||
setTimeout(() => {
|
throw new Error(`Failed to generate PDF: ${response.status}`);
|
||||||
alert(`✅ Sent ${quantity} labels to printer: ${printerName}\n📊 Order: ${prodOrder}`);
|
}
|
||||||
|
return response.blob();
|
||||||
// Update database status and refresh table
|
})
|
||||||
updatePrintedStatus(orderId);
|
.then(blob => {
|
||||||
}, 500);
|
// Create a temporary URL for the PDF
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePDFGeneration(selectedRow) {
|
function handlePDFGeneration(selectedRow) {
|
||||||
|
|||||||
@@ -1,422 +0,0 @@
|
|||||||
# Quality Control Management System - Documentation
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
1. [Login System](#login-system)
|
|
||||||
2. [Dashboard System](#dashboard-system)
|
|
||||||
3. [User Authentication](#user-authentication)
|
|
||||||
4. [Role-Based Access Control](#role-based-access-control)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Login System
|
|
||||||
|
|
||||||
### Overview
|
|
||||||
The Quality Control Management System features a dual-database authentication system that provides flexible user management and robust access control. The login system supports both internal SQLite database users and external MariaDB database users.
|
|
||||||
|
|
||||||
### Authentication Flow
|
|
||||||
|
|
||||||
#### 1. Login Page Access
|
|
||||||
- **URL**: `/login`
|
|
||||||
- **Template**: `login.html`
|
|
||||||
- **Methods**: `GET`, `POST`
|
|
||||||
|
|
||||||
#### 2. User Interface
|
|
||||||
The login page features:
|
|
||||||
- **Company Logo**: Displayed prominently on the left side
|
|
||||||
- **Login Form**: Clean, centered form on the right side
|
|
||||||
- **Required Fields**:
|
|
||||||
- Username (text input)
|
|
||||||
- Password (password input)
|
|
||||||
- **Responsive Design**: Adapts to different screen sizes
|
|
||||||
|
|
||||||
#### 3. Authentication Methods
|
|
||||||
|
|
||||||
##### Internal Database Authentication
|
|
||||||
Users can access the system using the internal SQLite database by prefixing their username with `#`:
|
|
||||||
|
|
||||||
**Format**: `#username`
|
|
||||||
**Example**: `#admin` for internal admin user
|
|
||||||
|
|
||||||
**Database Details**:
|
|
||||||
- **Location**: `py_app/instance/users.db`
|
|
||||||
- **Table**: `users`
|
|
||||||
- **Schema**: `username, password, role`
|
|
||||||
- **Use Case**: System administrators, fallback authentication
|
|
||||||
|
|
||||||
##### External Database Authentication
|
|
||||||
Standard authentication uses the external MariaDB database:
|
|
||||||
|
|
||||||
**Format**: `username` (no prefix)
|
|
||||||
**Example**: `john.doe` for external user
|
|
||||||
|
|
||||||
**Database Details**:
|
|
||||||
- **Type**: MariaDB
|
|
||||||
- **Configuration**: Loaded from `external_database_settings`
|
|
||||||
- **Table**: `users`
|
|
||||||
- **Schema**: `username, password, role`
|
|
||||||
- **Use Case**: Regular operational users
|
|
||||||
|
|
||||||
#### 4. Authentication Logic
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Authentication Process Flow
|
|
||||||
if username.startswith('#'):
|
|
||||||
# Internal SQLite Database Authentication
|
|
||||||
username_clean = username[1:].strip()
|
|
||||||
# Query: py_app/instance/users.db
|
|
||||||
|
|
||||||
else:
|
|
||||||
# External MariaDB Database Authentication
|
|
||||||
# Primary: External database query
|
|
||||||
# Fallback: Internal database if external fails
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5. Security Features
|
|
||||||
|
|
||||||
##### Input Validation
|
|
||||||
- **Required Fields**: Both username and password must be provided
|
|
||||||
- **Sanitization**: Automatic trimming of whitespace
|
|
||||||
- **Error Handling**: Clear error messages for invalid inputs
|
|
||||||
|
|
||||||
##### Database Connection Security
|
|
||||||
- **Dual Fallback**: External database with internal fallback
|
|
||||||
- **Error Isolation**: Database errors don't expose system details
|
|
||||||
- **Connection Management**: Proper connection opening/closing
|
|
||||||
|
|
||||||
##### Session Management
|
|
||||||
- **Secure Sessions**: User credentials stored in Flask session
|
|
||||||
- **Role Tracking**: User role preserved for authorization
|
|
||||||
- **Session Data**:
|
|
||||||
- `session['user']`: Username
|
|
||||||
- `session['role']`: User role
|
|
||||||
|
|
||||||
#### 6. User Roles
|
|
||||||
|
|
||||||
The system supports multiple user roles with different access levels:
|
|
||||||
|
|
||||||
- **superadmin**: Full system access, all modules and administrative functions
|
|
||||||
- **admin**: Administrative access with some limitations
|
|
||||||
- **quality**: Quality control module access
|
|
||||||
- **warehouse**: Warehouse management module access
|
|
||||||
- **scan**: Scanning operations access
|
|
||||||
- **etichete**: Label management access
|
|
||||||
- **management**: Management reporting and oversight
|
|
||||||
|
|
||||||
#### 7. Login Process
|
|
||||||
|
|
||||||
1. **User Navigation**: User accesses `/login` URL
|
|
||||||
2. **Form Display**: Login form rendered with company branding
|
|
||||||
3. **Credential Submission**: User enters username/password and submits
|
|
||||||
4. **Authentication Check**:
|
|
||||||
- Internal users: Check SQLite database
|
|
||||||
- External users: Check MariaDB database with SQLite fallback
|
|
||||||
5. **Session Creation**: Valid credentials create user session
|
|
||||||
6. **Redirect**: Successful login redirects to `/dashboard`
|
|
||||||
7. **Error Handling**: Invalid credentials display error message
|
|
||||||
|
|
||||||
#### 8. Error Messages
|
|
||||||
|
|
||||||
- **Missing Credentials**: "Please enter both username and password."
|
|
||||||
- **Invalid Credentials**: "Invalid credentials. Please try again."
|
|
||||||
- **Database Errors**: Handled gracefully with fallback mechanisms
|
|
||||||
|
|
||||||
#### 9. Post-Login Behavior
|
|
||||||
|
|
||||||
After successful authentication:
|
|
||||||
- **Session Establishment**: User session created with username and role
|
|
||||||
- **Dashboard Redirect**: User redirected to main dashboard
|
|
||||||
- **Access Control**: Role-based permissions applied throughout system
|
|
||||||
- **Navigation**: Header displays logged-in user information
|
|
||||||
|
|
||||||
#### 10. Security Considerations
|
|
||||||
|
|
||||||
##### Password Security
|
|
||||||
- **Storage**: Passwords stored in plaintext (consider encryption upgrade)
|
|
||||||
- **Transmission**: Form-based submission over HTTPS recommended
|
|
||||||
- **Session**: Password not stored in session, only username/role
|
|
||||||
|
|
||||||
##### Database Security
|
|
||||||
- **Connection Strings**: External database settings in separate config
|
|
||||||
- **Error Handling**: Database errors logged but not exposed to users
|
|
||||||
- **Fallback System**: Ensures availability even if external database fails
|
|
||||||
|
|
||||||
### Technical Implementation
|
|
||||||
|
|
||||||
#### Frontend Components
|
|
||||||
- **Template**: `templates/login.html`
|
|
||||||
- **Styling**: Login-specific CSS in `static/style.css`
|
|
||||||
- **Assets**: Company logo (`static/logo_login.jpg`)
|
|
||||||
|
|
||||||
#### Backend Components
|
|
||||||
- **Route Handler**: `@bp.route('/login', methods=['GET', 'POST'])`
|
|
||||||
- **Database Connections**: SQLite and MariaDB integration
|
|
||||||
- **Session Management**: Flask session handling
|
|
||||||
- **Error Handling**: Comprehensive exception management
|
|
||||||
|
|
||||||
#### Configuration Files
|
|
||||||
- **External Database**: Configuration loaded from `external_database_settings`
|
|
||||||
- **Internal Database**: SQLite database in `instance/users.db`
|
|
||||||
|
|
||||||
### Usage Examples
|
|
||||||
|
|
||||||
#### Standard User Login
|
|
||||||
```
|
|
||||||
Username: john.doe
|
|
||||||
Password: userpassword
|
|
||||||
Result: Queries external MariaDB database
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Internal Admin Login
|
|
||||||
```
|
|
||||||
Username: #admin
|
|
||||||
Password: adminpassword
|
|
||||||
Result: Queries internal SQLite database
|
|
||||||
```
|
|
||||||
|
|
||||||
#### System Administrator Login
|
|
||||||
```
|
|
||||||
Username: #superadmin
|
|
||||||
Password: superpass
|
|
||||||
Result: Internal database, full system access
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Dashboard System
|
|
||||||
|
|
||||||
### Overview
|
|
||||||
The dashboard serves as the central hub of the Quality Control Management System, providing authenticated users with access to various system modules based on their assigned roles. It features a clean, card-based interface that displays available modules and ensures proper access control.
|
|
||||||
|
|
||||||
### Dashboard Access
|
|
||||||
|
|
||||||
#### 1. Dashboard Page Access
|
|
||||||
- **URL**: `/dashboard`
|
|
||||||
- **Template**: `dashboard.html`
|
|
||||||
- **Methods**: `GET`
|
|
||||||
- **Authentication Required**: Yes (redirects to login if not authenticated)
|
|
||||||
|
|
||||||
#### 2. User Interface Design
|
|
||||||
|
|
||||||
The dashboard features a modern, responsive card-based layout:
|
|
||||||
- **Container**: Full-width responsive grid layout
|
|
||||||
- **Module Cards**: Individual cards for each system module
|
|
||||||
- **Visual Hierarchy**: Clear headings, descriptions, and call-to-action buttons
|
|
||||||
- **Responsive Design**: Adapts to different screen sizes and devices
|
|
||||||
|
|
||||||
#### 3. Available Modules
|
|
||||||
|
|
||||||
##### Scanning Module
|
|
||||||
- **Card Title**: "Access Scanning Module"
|
|
||||||
- **Description**: "Final scanning module for production orders"
|
|
||||||
- **Button**: "Launch Scanning Module"
|
|
||||||
- **Route**: `/scan`
|
|
||||||
- **Required Roles**: `superadmin`, `scan`
|
|
||||||
- **Purpose**: Quality control scanning operations for production orders
|
|
||||||
|
|
||||||
##### Reports Module (Quality)
|
|
||||||
- **Card Title**: "Access Reports Module"
|
|
||||||
- **Description**: "Module for verification and quality settings configuration"
|
|
||||||
- **Button**: "Launch Reports Module"
|
|
||||||
- **Route**: `/quality`
|
|
||||||
- **Required Roles**: `superadmin`, `quality`
|
|
||||||
- **Purpose**: Quality reporting, defects analysis, and quality control reports
|
|
||||||
|
|
||||||
##### Warehouse Module
|
|
||||||
- **Card Title**: "Access Warehouse Module"
|
|
||||||
- **Description**: "Access warehouse module functionalities"
|
|
||||||
- **Button**: "Open Warehouse"
|
|
||||||
- **Route**: `/warehouse`
|
|
||||||
- **Required Roles**: `superadmin`, `warehouse`
|
|
||||||
- **Purpose**: Warehouse management operations and inventory control
|
|
||||||
|
|
||||||
##### Labels Module
|
|
||||||
- **Card Title**: "Access Labels Module"
|
|
||||||
- **Description**: "Module for label management"
|
|
||||||
- **Button**: "Launch Labels Module"
|
|
||||||
- **Route**: `/etichete`
|
|
||||||
- **Required Roles**: `superadmin`, `etichete`
|
|
||||||
- **Purpose**: Label creation, template management, and printing operations
|
|
||||||
|
|
||||||
##### Settings Module
|
|
||||||
- **Card Title**: "Manage Settings"
|
|
||||||
- **Description**: "Access and manage application settings"
|
|
||||||
- **Button**: "Access Settings Page"
|
|
||||||
- **Route**: `/settings`
|
|
||||||
- **Required Roles**: `superadmin` only
|
|
||||||
- **Purpose**: System configuration, user management, and administrative settings
|
|
||||||
|
|
||||||
#### 4. Access Control Logic
|
|
||||||
|
|
||||||
The dashboard implements role-based access control at both the display and route levels:
|
|
||||||
|
|
||||||
##### Frontend Display Control
|
|
||||||
All module cards are displayed to all authenticated users, but access is controlled at the route level.
|
|
||||||
|
|
||||||
##### Backend Route Protection
|
|
||||||
Each module route implements permission checking:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Quality Module Access Control
|
|
||||||
@bp.route('/quality')
|
|
||||||
def quality():
|
|
||||||
if 'role' not in session or session['role'] not in ['superadmin', 'quality']:
|
|
||||||
flash('Access denied: Quality users only.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
|
|
||||||
# Warehouse Module Access Control
|
|
||||||
@bp.route('/warehouse')
|
|
||||||
def warehouse():
|
|
||||||
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse']:
|
|
||||||
flash('Access denied: Warehouse users only.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
|
|
||||||
# Scanning Module Access Control
|
|
||||||
@bp.route('/scan')
|
|
||||||
def scan():
|
|
||||||
if 'role' not in session or session['role'] not in ['superadmin', 'scan']:
|
|
||||||
flash('Access denied: Scan users only.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
|
|
||||||
# Labels Module Access Control
|
|
||||||
@bp.route('/etichete')
|
|
||||||
def etichete():
|
|
||||||
if 'role' not in session or session['role'] not in ['superadmin', 'etichete']:
|
|
||||||
flash('Access denied: Etichete users only.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
|
|
||||||
# Settings Module Access Control (Superadmin Only)
|
|
||||||
def settings_handler():
|
|
||||||
if 'role' not in session or session['role'] != 'superadmin':
|
|
||||||
flash('Access denied: Superadmin only.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5. User Experience Flow
|
|
||||||
|
|
||||||
1. **Authentication Check**: User must be logged in to access dashboard
|
|
||||||
2. **Dashboard Display**: All module cards shown regardless of role
|
|
||||||
3. **Module Selection**: User clicks on desired module button
|
|
||||||
4. **Permission Validation**: System checks user role against module requirements
|
|
||||||
5. **Access Grant/Deny**:
|
|
||||||
- **Authorized**: User redirected to module interface
|
|
||||||
- **Unauthorized**: Error message displayed, user remains on dashboard
|
|
||||||
|
|
||||||
#### 6. Session Management
|
|
||||||
|
|
||||||
The dashboard relies on Flask session management:
|
|
||||||
- **Session Check**: `if 'user' not in session` validates authentication
|
|
||||||
- **Role Access**: `session['role']` determines module permissions
|
|
||||||
- **Debug Logging**: Session information logged for troubleshooting
|
|
||||||
|
|
||||||
#### 7. Error Handling
|
|
||||||
|
|
||||||
##### Authentication Errors
|
|
||||||
- **Unauthenticated Users**: Automatic redirect to login page
|
|
||||||
- **Session Timeout**: Redirect to login with appropriate messaging
|
|
||||||
|
|
||||||
##### Authorization Errors
|
|
||||||
- **Insufficient Permissions**: Flash message with specific role requirements
|
|
||||||
- **Access Denied**: User returned to dashboard with error notification
|
|
||||||
- **Clear Messaging**: Specific error messages indicate required permissions
|
|
||||||
|
|
||||||
#### 8. Security Features
|
|
||||||
|
|
||||||
##### Session Security
|
|
||||||
- **Authentication Required**: All dashboard access requires valid session
|
|
||||||
- **Role Validation**: Each module validates user role before access
|
|
||||||
- **Automatic Redirect**: Unauthorized access redirected safely
|
|
||||||
|
|
||||||
##### Access Control
|
|
||||||
- **Principle of Least Privilege**: Users only access modules for their role
|
|
||||||
- **Superadmin Override**: Superadmin role has access to all modules
|
|
||||||
- **Route-Level Protection**: Backend validation prevents unauthorized access
|
|
||||||
|
|
||||||
#### 9. Module Descriptions
|
|
||||||
|
|
||||||
##### Quality Reports Module
|
|
||||||
- **Primary Function**: Generate quality control reports and analytics
|
|
||||||
- **Key Features**:
|
|
||||||
- Daily, weekly, and custom date range reports
|
|
||||||
- Quality defects analysis and tracking
|
|
||||||
- Export capabilities (CSV format)
|
|
||||||
- Database testing tools (superadmin only)
|
|
||||||
|
|
||||||
##### Scanning Module
|
|
||||||
- **Primary Function**: Production order scanning and quality validation
|
|
||||||
- **Key Features**:
|
|
||||||
- Barcode/QR code scanning interface
|
|
||||||
- Real-time quality validation
|
|
||||||
- Production order processing
|
|
||||||
|
|
||||||
##### Warehouse Module
|
|
||||||
- **Primary Function**: Warehouse operations and inventory management
|
|
||||||
- **Key Features**:
|
|
||||||
- Inventory tracking and management
|
|
||||||
- Location management
|
|
||||||
- Warehouse reporting
|
|
||||||
|
|
||||||
##### Labels Module
|
|
||||||
- **Primary Function**: Label design, generation, and printing
|
|
||||||
- **Key Features**:
|
|
||||||
- Label template creation and management
|
|
||||||
- Dynamic label generation
|
|
||||||
- Print management system
|
|
||||||
|
|
||||||
##### Settings Module
|
|
||||||
- **Primary Function**: System administration and configuration
|
|
||||||
- **Key Features**:
|
|
||||||
- User account management
|
|
||||||
- Role and permission configuration
|
|
||||||
- Database settings management
|
|
||||||
- System configuration options
|
|
||||||
|
|
||||||
#### 10. Technical Implementation
|
|
||||||
|
|
||||||
##### Frontend Components
|
|
||||||
- **Template**: `templates/dashboard.html`
|
|
||||||
- **Styling**: Dashboard-specific CSS classes in `static/style.css`
|
|
||||||
- **Layout**: CSS Grid/Flexbox responsive card layout
|
|
||||||
- **Navigation**: Base template integration with header/footer
|
|
||||||
|
|
||||||
##### Backend Components
|
|
||||||
- **Route Handler**: `@bp.route('/dashboard')`
|
|
||||||
- **Session Management**: Flask session integration
|
|
||||||
- **Authentication Check**: User session validation
|
|
||||||
- **Logging**: Debug output for troubleshooting
|
|
||||||
|
|
||||||
##### Styling Classes
|
|
||||||
- `.dashboard-container`: Main container with responsive grid
|
|
||||||
- `.dashboard-card`: Individual module cards
|
|
||||||
- `.btn`: Standardized button styling
|
|
||||||
- Responsive breakpoints for mobile/tablet adaptation
|
|
||||||
|
|
||||||
### Usage Examples
|
|
||||||
|
|
||||||
#### Superadmin User Dashboard Access
|
|
||||||
```
|
|
||||||
Role: superadmin
|
|
||||||
Available Modules: All (Scanning, Quality, Warehouse, Labels, Settings)
|
|
||||||
Special Access: Settings module exclusive access
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Quality Control User Dashboard Access
|
|
||||||
```
|
|
||||||
Role: quality
|
|
||||||
Available Modules: Quality Reports module only
|
|
||||||
Restricted Access: Cannot access other modules
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Multi-Role Access Example
|
|
||||||
```
|
|
||||||
Role: warehouse
|
|
||||||
Available Modules: Warehouse module only
|
|
||||||
Access Pattern: Click → Permission Check → Module Access
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*This documentation covers the dashboard system implementation. For specific module details, see their respective documentation sections.*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Windows Print Service Connection Tester
|
|
||||||
Tests connectivity between Flask server and Windows Print Service
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import time
|
|
||||||
from app.print_config import WINDOWS_PRINT_SERVICE_URL, PRINT_SERVICE_TIMEOUT
|
|
||||||
|
|
||||||
def test_print_service_connection():
|
|
||||||
"""Test connection to Windows Print Service"""
|
|
||||||
print("=== Windows Print Service Connection Test ===\n")
|
|
||||||
|
|
||||||
print(f"🔍 Testing connection to: {WINDOWS_PRINT_SERVICE_URL}")
|
|
||||||
print(f"⏱️ Timeout: {PRINT_SERVICE_TIMEOUT} seconds")
|
|
||||||
print(f"🌐 From: Flask server (Linux)")
|
|
||||||
print(f"🎯 To: Windows Print Service")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Test 1: Health check
|
|
||||||
print("1️⃣ Testing /health endpoint...")
|
|
||||||
try:
|
|
||||||
start_time = time.time()
|
|
||||||
response = requests.get(
|
|
||||||
f"{WINDOWS_PRINT_SERVICE_URL}/health",
|
|
||||||
timeout=PRINT_SERVICE_TIMEOUT
|
|
||||||
)
|
|
||||||
elapsed = time.time() - start_time
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
data = response.json()
|
|
||||||
print(f" ✅ SUCCESS (Status: {response.status_code}, Time: {elapsed:.2f}s)")
|
|
||||||
print(f" 📋 Service: {data.get('service', 'Unknown')}")
|
|
||||||
print(f" 🖥️ Platform: {data.get('platform', 'Unknown')}")
|
|
||||||
print(f" 📅 Timestamp: {data.get('timestamp', 'Unknown')}")
|
|
||||||
health_ok = True
|
|
||||||
else:
|
|
||||||
print(f" ❌ FAILED (Status: {response.status_code})")
|
|
||||||
print(f" 📝 Response: {response.text}")
|
|
||||||
health_ok = False
|
|
||||||
except requests.exceptions.ConnectionError as e:
|
|
||||||
print(f" ❌ CONNECTION REFUSED")
|
|
||||||
print(f" 🔧 Error: {str(e)}")
|
|
||||||
print(f" 💡 This usually means:")
|
|
||||||
print(f" • Windows service is not running")
|
|
||||||
print(f" • Wrong IP address in configuration")
|
|
||||||
print(f" • Firewall blocking connection")
|
|
||||||
print(f" • Network routing issue")
|
|
||||||
health_ok = False
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ❌ ERROR: {str(e)}")
|
|
||||||
health_ok = False
|
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Test 2: Printers endpoint (only if health check passed)
|
|
||||||
if health_ok:
|
|
||||||
print("2️⃣ Testing /printers endpoint...")
|
|
||||||
try:
|
|
||||||
response = requests.get(
|
|
||||||
f"{WINDOWS_PRINT_SERVICE_URL}/printers",
|
|
||||||
timeout=PRINT_SERVICE_TIMEOUT
|
|
||||||
)
|
|
||||||
if response.status_code == 200:
|
|
||||||
data = response.json()
|
|
||||||
printers = data.get('printers', [])
|
|
||||||
print(f" ✅ SUCCESS - Found {len(printers)} printer(s)")
|
|
||||||
for i, printer in enumerate(printers[:3]): # Show first 3
|
|
||||||
name = printer.get('name', 'Unknown') if isinstance(printer, dict) else printer
|
|
||||||
print(f" 🖨️ {i+1}. {name}")
|
|
||||||
if len(printers) > 3:
|
|
||||||
print(f" ... and {len(printers)-3} more")
|
|
||||||
else:
|
|
||||||
print(f" ❌ FAILED (Status: {response.status_code})")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ❌ ERROR: {str(e)}")
|
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Test 3: Network diagnostics
|
|
||||||
print("3️⃣ Network Diagnostics...")
|
|
||||||
|
|
||||||
# Parse URL to get host and port
|
|
||||||
try:
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
parsed = urlparse(WINDOWS_PRINT_SERVICE_URL)
|
|
||||||
host = parsed.hostname
|
|
||||||
port = parsed.port or 8765
|
|
||||||
|
|
||||||
print(f" 🌐 Target Host: {host}")
|
|
||||||
print(f" 🔌 Target Port: {port}")
|
|
||||||
|
|
||||||
# Test DNS resolution
|
|
||||||
try:
|
|
||||||
import socket
|
|
||||||
ip = socket.gethostbyname(host)
|
|
||||||
print(f" 🔍 DNS Resolution: {host} → {ip}")
|
|
||||||
dns_ok = True
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ❌ DNS Resolution failed: {e}")
|
|
||||||
dns_ok = False
|
|
||||||
|
|
||||||
# Test raw TCP connection
|
|
||||||
if dns_ok:
|
|
||||||
try:
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sock.settimeout(5)
|
|
||||||
result = sock.connect_ex((host, port))
|
|
||||||
sock.close()
|
|
||||||
|
|
||||||
if result == 0:
|
|
||||||
print(f" ✅ TCP Connection: Port {port} is open")
|
|
||||||
else:
|
|
||||||
print(f" ❌ TCP Connection: Port {port} is closed or filtered")
|
|
||||||
print(f" 💡 Check Windows Firewall and service status")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ❌ TCP Test error: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ❌ URL parsing error: {e}")
|
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Final recommendations
|
|
||||||
print("🔧 TROUBLESHOOTING RECOMMENDATIONS:")
|
|
||||||
print()
|
|
||||||
|
|
||||||
if not health_ok:
|
|
||||||
print("❌ CONNECTION FAILED - Try these steps:")
|
|
||||||
print("1. Verify Windows Print Service is running:")
|
|
||||||
print(" • Windows: sc query QualityLabelPrinting")
|
|
||||||
print(" • Or: Get-Service QualityLabelPrinting")
|
|
||||||
print()
|
|
||||||
print("2. Check Windows machine IP address:")
|
|
||||||
print(" • Windows: ipconfig | findstr IPv4")
|
|
||||||
print(" • Update WINDOWS_PRINT_SERVICE_URL in print_config.py")
|
|
||||||
print()
|
|
||||||
print("3. Test firewall:")
|
|
||||||
print(" • Windows: Allow port 8765 in Windows Firewall")
|
|
||||||
print(" • Test: telnet [WINDOWS_IP] 8765")
|
|
||||||
print()
|
|
||||||
print("4. If same machine, verify service binding:")
|
|
||||||
print(" • Service should bind to 0.0.0.0:8765 (not 127.0.0.1)")
|
|
||||||
else:
|
|
||||||
print("✅ CONNECTION SUCCESS - Print service should work!")
|
|
||||||
print("• Service is reachable and responding")
|
|
||||||
print("• Ready for label printing")
|
|
||||||
print("• Try printing labels from the web interface")
|
|
||||||
|
|
||||||
print(f"\n📝 Current Configuration:")
|
|
||||||
print(f" Service URL: {WINDOWS_PRINT_SERVICE_URL}")
|
|
||||||
print(f" Timeout: {PRINT_SERVICE_TIMEOUT}s")
|
|
||||||
|
|
||||||
return health_ok
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_print_service_connection()
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
# Recticel Direct Print Service - Installation Guide
|
|
||||||
|
|
||||||
## 🎯 Overview
|
|
||||||
|
|
||||||
This service enables direct printing from web browsers to thermal label printers (Epson TM-T20, Citizen CTS-310, etc.) without requiring PDF generation or manual printer dialogs.
|
|
||||||
|
|
||||||
## 🔧 Installation Steps
|
|
||||||
|
|
||||||
### 1. **Build the C# Application**
|
|
||||||
|
|
||||||
**Requirements:**
|
|
||||||
- Visual Studio 2019 or later
|
|
||||||
- .NET Framework 4.7.2 or later
|
|
||||||
- Windows 10/11
|
|
||||||
|
|
||||||
**Build Steps:**
|
|
||||||
1. Open `RecticelPrintService.sln` in Visual Studio
|
|
||||||
2. Restore NuGet packages (Newtonsoft.Json)
|
|
||||||
3. Build in Release mode
|
|
||||||
4. Copy the entire `bin\Release` folder to `C:\RecticelPrintService\`
|
|
||||||
|
|
||||||
### 2. **Register the Custom Protocol**
|
|
||||||
|
|
||||||
**Option A: Double-click the registry file**
|
|
||||||
1. Right-click `recticel-print-protocol.reg`
|
|
||||||
2. Select "Run as administrator"
|
|
||||||
3. Confirm the registry import
|
|
||||||
|
|
||||||
**Option B: Manual registry import**
|
|
||||||
1. Open Command Prompt as Administrator
|
|
||||||
2. Run: `reg import "recticel-print-protocol.reg"`
|
|
||||||
|
|
||||||
### 3. **Install Printer Drivers**
|
|
||||||
|
|
||||||
**For Epson TM-T20:**
|
|
||||||
- Download and install Epson TM-T20 drivers from Epson website
|
|
||||||
- Configure printer as "Epson TM-T20" in Windows
|
|
||||||
|
|
||||||
**For Citizen CTS-310:**
|
|
||||||
- Download and install Citizen CTS-310 drivers
|
|
||||||
- Configure printer as "Citizen CTS-310" in Windows
|
|
||||||
|
|
||||||
### 4. **Configure Printer Settings**
|
|
||||||
|
|
||||||
1. Open Windows "Printers & scanners"
|
|
||||||
2. Set paper size to **80mm x 110mm** (or closest available)
|
|
||||||
3. Set print quality to **300 DPI** for thermal printers
|
|
||||||
4. Disable margins (set to 0 if possible)
|
|
||||||
|
|
||||||
### 5. **Test the Installation**
|
|
||||||
|
|
||||||
1. Open your web browser
|
|
||||||
2. Navigate to the print module page
|
|
||||||
3. Select an order
|
|
||||||
4. Choose "Direct Print" option
|
|
||||||
5. Select your thermal printer
|
|
||||||
6. Click "Print Labels"
|
|
||||||
|
|
||||||
## 🖨️ Printer Configuration
|
|
||||||
|
|
||||||
### Label Size Settings
|
|
||||||
- **Width**: 80mm
|
|
||||||
- **Height**: 110mm
|
|
||||||
- **Margins**: 0mm (all sides)
|
|
||||||
- **Print Quality**: 300 DPI
|
|
||||||
|
|
||||||
### Thermal Printer Specific Settings
|
|
||||||
|
|
||||||
**Epson TM-T20:**
|
|
||||||
- Paper Type: Continuous label
|
|
||||||
- Print Speed: Normal
|
|
||||||
- Print Density: Adjust based on label stock
|
|
||||||
|
|
||||||
**Citizen CTS-310:**
|
|
||||||
- Media Type: Label
|
|
||||||
- Print Method: Thermal Transfer or Direct Thermal
|
|
||||||
- Print Speed: Medium
|
|
||||||
|
|
||||||
## 🔄 How It Works
|
|
||||||
|
|
||||||
1. **Web Interface**: User selects order and clicks "Direct Print"
|
|
||||||
2. **Data Encoding**: Order data is encoded as Base64 JSON
|
|
||||||
3. **Custom Protocol**: Browser calls `recticel-print://printer/data`
|
|
||||||
4. **Service Activation**: Windows launches RecticelPrintService.exe
|
|
||||||
5. **Data Processing**: Service decodes and formats label data
|
|
||||||
6. **Direct Printing**: Label is sent directly to the thermal printer
|
|
||||||
7. **Status Update**: Database is updated to mark order as printed
|
|
||||||
|
|
||||||
## 🚀 URL Format
|
|
||||||
|
|
||||||
```
|
|
||||||
recticel-print://[printer_name]/[base64_encoded_json]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```
|
|
||||||
recticel-print://Epson%20TM-T20/eyJvcmRlcl9pZCI6MTIzLCJjb21hbmRhX3Byb2R1Y3RpZSI6IkNQMDAwMDA3MTEifQ==
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⚠️ Troubleshooting
|
|
||||||
|
|
||||||
### Service Not Starting
|
|
||||||
1. Check if the service is installed in `C:\RecticelPrintService\`
|
|
||||||
2. Verify registry entries are correct
|
|
||||||
3. Run as Administrator if needed
|
|
||||||
|
|
||||||
### Printer Not Found
|
|
||||||
1. Check printer name in Windows exactly matches selection
|
|
||||||
2. Ensure printer drivers are installed
|
|
||||||
3. Test print from Windows to verify printer works
|
|
||||||
|
|
||||||
### Labels Not Printing
|
|
||||||
1. Verify paper size is set to 80x110mm
|
|
||||||
2. Check printer margins are set to 0
|
|
||||||
3. Ensure thermal paper is loaded correctly
|
|
||||||
|
|
||||||
### Browser Security
|
|
||||||
- Some browsers may block custom protocols
|
|
||||||
- Add site to trusted sites if needed
|
|
||||||
- Chrome may show a confirmation dialog
|
|
||||||
|
|
||||||
## 🔧 Advanced Configuration
|
|
||||||
|
|
||||||
### Custom Printer Names
|
|
||||||
Users can add custom printer names by selecting "Other Printer..." and entering the exact Windows printer name.
|
|
||||||
|
|
||||||
### Multiple Label Quantities
|
|
||||||
The service automatically prints the quantity specified in the order, with sequential numbering (CP00000711-001, CP00000711-002, etc.).
|
|
||||||
|
|
||||||
### Fallback to PDF
|
|
||||||
If direct printing fails, users can switch to "Generate PDF" mode for manual printing.
|
|
||||||
|
|
||||||
## 📞 Support
|
|
||||||
|
|
||||||
For technical support:
|
|
||||||
1. Check Windows Event Viewer for service errors
|
|
||||||
2. Verify printer connectivity and driver installation
|
|
||||||
3. Test with PDF generation mode as fallback
|
|
||||||
4. Contact IT support with specific error messages
|
|
||||||
|
|
||||||
## 🔒 Security Notes
|
|
||||||
|
|
||||||
- The service only processes label data from trusted sources
|
|
||||||
- Registry changes require Administrator privileges
|
|
||||||
- Service runs with current user permissions
|
|
||||||
- No network connections are made by the service
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 15
|
|
||||||
VisualStudioVersion = 15.0.28010.2046
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RecticelPrintService", "RecticelPrintService\RecticelPrintService.csproj", "{41ED6235-3C76-4723-A5F6-02D2FA51E677}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{41ED6235-3C76-4723-A5F6-02D2FA51E677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{41ED6235-3C76-4723-A5F6-02D2FA51E677}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{41ED6235-3C76-4723-A5F6-02D2FA51E677}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{41ED6235-3C76-4723-A5F6-02D2FA51E677}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {FC401D2A-E68E-47EE-B315-E1A2F0B292FA}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<startup>
|
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
|
||||||
</startup>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Printing;
|
|
||||||
using System.Text;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace RecticelPrintService
|
|
||||||
{
|
|
||||||
public class LabelPrinter
|
|
||||||
{
|
|
||||||
private PrintData _labelData;
|
|
||||||
|
|
||||||
// Label dimensions in millimeters (80x110mm)
|
|
||||||
private const float LABEL_WIDTH_MM = 80f;
|
|
||||||
private const float LABEL_HEIGHT_MM = 110f;
|
|
||||||
|
|
||||||
// Convert mm to hundredths of an inch (used by .NET printing)
|
|
||||||
private float MmToHundredthsOfInch(float mm)
|
|
||||||
{
|
|
||||||
return mm * 3.937f; // 1mm = 0.03937 inches, * 100 for hundredths
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool PrintLabel(string printerName, string jsonData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Parse JSON data
|
|
||||||
_labelData = JsonConvert.DeserializeObject<PrintData>(jsonData);
|
|
||||||
|
|
||||||
if (_labelData == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Failed to parse JSON data");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up print document
|
|
||||||
PrintDocument printDoc = new PrintDocument();
|
|
||||||
printDoc.PrinterSettings.PrinterName = printerName;
|
|
||||||
|
|
||||||
// Check if printer exists
|
|
||||||
if (!printDoc.PrinterSettings.IsValid)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Printer '{printerName}' not found or not available");
|
|
||||||
Console.WriteLine("Available printers:");
|
|
||||||
foreach (string printer in PrinterSettings.InstalledPrinters)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" - {printer}");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set paper size for 80x110mm labels
|
|
||||||
PaperSize labelSize = new PaperSize("Label 80x110mm",
|
|
||||||
(int)MmToHundredthsOfInch(LABEL_WIDTH_MM),
|
|
||||||
(int)MmToHundredthsOfInch(LABEL_HEIGHT_MM));
|
|
||||||
|
|
||||||
printDoc.DefaultPageSettings.PaperSize = labelSize;
|
|
||||||
printDoc.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
// Set print event handler
|
|
||||||
printDoc.PrintPage += PrintDoc_PrintPage;
|
|
||||||
|
|
||||||
// Print the document
|
|
||||||
printDoc.Print();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error printing label: {ex.Message}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Graphics g = e.Graphics;
|
|
||||||
|
|
||||||
// Define fonts
|
|
||||||
Font companyFont = new Font("Arial", 10, FontStyle.Bold);
|
|
||||||
Font headerFont = new Font("Arial", 8, FontStyle.Bold);
|
|
||||||
Font dataFont = new Font("Arial", 9, FontStyle.Bold);
|
|
||||||
Font labelFont = new Font("Arial", 7);
|
|
||||||
Font barcodeFont = new Font("Courier New", 6, FontStyle.Bold);
|
|
||||||
|
|
||||||
// Define brushes and pens
|
|
||||||
Brush textBrush = Brushes.Black;
|
|
||||||
Pen borderPen = new Pen(Color.Black, 2);
|
|
||||||
Pen dividerPen = new Pen(Color.Gray, 1);
|
|
||||||
|
|
||||||
// Calculate positions (in pixels, 300 DPI assumed)
|
|
||||||
float dpiX = e.Graphics.DpiX;
|
|
||||||
float dpiY = e.Graphics.DpiY;
|
|
||||||
|
|
||||||
// Convert mm to pixels
|
|
||||||
float labelWidth = (LABEL_WIDTH_MM / 25.4f) * dpiX;
|
|
||||||
float labelHeight = (LABEL_HEIGHT_MM / 25.4f) * dpiY;
|
|
||||||
|
|
||||||
// Content area (similar to your HTML preview)
|
|
||||||
float contentX = (3f / 25.4f) * dpiX; // 3mm margin
|
|
||||||
float contentY = (15f / 25.4f) * dpiY; // 15mm from bottom
|
|
||||||
float contentWidth = (60f / 25.4f) * dpiX; // 60mm width
|
|
||||||
float contentHeight = (85f / 25.4f) * dpiY; // 85mm height
|
|
||||||
|
|
||||||
// Draw main content border
|
|
||||||
g.DrawRectangle(borderPen, contentX, contentY, contentWidth, contentHeight);
|
|
||||||
|
|
||||||
// Calculate row height
|
|
||||||
float rowHeight = contentHeight / 10f; // 9 rows + spacing
|
|
||||||
float currentY = contentY;
|
|
||||||
|
|
||||||
// Row 1: Company Header
|
|
||||||
string companyText = "INNOFA RROMANIA SRL";
|
|
||||||
SizeF companySize = g.MeasureString(companyText, companyFont);
|
|
||||||
float companyX = contentX + (contentWidth - companySize.Width) / 2;
|
|
||||||
g.DrawString(companyText, companyFont, textBrush, companyX, currentY + rowHeight/3);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider after company
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 2: Customer Name
|
|
||||||
string customerName = _labelData.CustomerName ?? "N/A";
|
|
||||||
SizeF customerSize = g.MeasureString(customerName, headerFont);
|
|
||||||
float customerX = contentX + (contentWidth - customerSize.Width) / 2;
|
|
||||||
g.DrawString(customerName, headerFont, textBrush, customerX, currentY + rowHeight/3);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider after customer
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Vertical divider (40% from left)
|
|
||||||
float verticalDividerX = contentX + contentWidth * 0.4f;
|
|
||||||
g.DrawLine(dividerPen, verticalDividerX, currentY, verticalDividerX, contentY + contentHeight);
|
|
||||||
|
|
||||||
// Row 3: Quantity
|
|
||||||
g.DrawString("Quantity ordered", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string quantity = _labelData.Cantitate.ToString();
|
|
||||||
SizeF qtySize = g.MeasureString(quantity, dataFont);
|
|
||||||
float qtyX = verticalDividerX + (contentWidth * 0.6f - qtySize.Width) / 2;
|
|
||||||
g.DrawString(quantity, dataFont, textBrush, qtyX, currentY + rowHeight/4);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 4: Customer Order
|
|
||||||
g.DrawString("Customer order", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string customerOrder = $"{_labelData.ComAchizClient}-{_labelData.NrLinieComClient}";
|
|
||||||
if (string.IsNullOrEmpty(_labelData.ComAchizClient)) customerOrder = "N/A";
|
|
||||||
SizeF orderSize = g.MeasureString(customerOrder, dataFont);
|
|
||||||
float orderX = verticalDividerX + (contentWidth * 0.6f - orderSize.Width) / 2;
|
|
||||||
g.DrawString(customerOrder, dataFont, textBrush, orderX, currentY + rowHeight/4);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 5: Delivery Date
|
|
||||||
g.DrawString("Delivery date", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string deliveryDate = _labelData.DataLivrare ?? "N/A";
|
|
||||||
if (!string.IsNullOrEmpty(deliveryDate) && deliveryDate != "N/A")
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DateTime dt = DateTime.Parse(deliveryDate);
|
|
||||||
deliveryDate = dt.ToString("dd/MM/yyyy");
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
SizeF dateSize = g.MeasureString(deliveryDate, dataFont);
|
|
||||||
float dateX = verticalDividerX + (contentWidth * 0.6f - dateSize.Width) / 2;
|
|
||||||
g.DrawString(deliveryDate, dataFont, textBrush, dateX, currentY + rowHeight/4);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 6: Description (double height)
|
|
||||||
g.DrawString("Description", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string description = _labelData.DescrComProd ?? "N/A";
|
|
||||||
// Word wrap description if needed
|
|
||||||
RectangleF descRect = new RectangleF(verticalDividerX + 5, currentY + 5,
|
|
||||||
contentWidth * 0.6f - 10, rowHeight * 2 - 10);
|
|
||||||
g.DrawString(description, dataFont, textBrush, descRect);
|
|
||||||
currentY += rowHeight * 2;
|
|
||||||
|
|
||||||
// Horizontal divider
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 7: Size
|
|
||||||
g.DrawString("Size", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string size = _labelData.Dimensiune ?? "N/A";
|
|
||||||
SizeF sizeSize = g.MeasureString(size, dataFont);
|
|
||||||
float sizeX = verticalDividerX + (contentWidth * 0.6f - sizeSize.Width) / 2;
|
|
||||||
g.DrawString(size, dataFont, textBrush, sizeX, currentY + rowHeight/4);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 8: Article Code
|
|
||||||
g.DrawString("Article Code", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string articleCode = _labelData.CustomerArticleNumber ?? "N/A";
|
|
||||||
SizeF artSize = g.MeasureString(articleCode, dataFont);
|
|
||||||
float artX = verticalDividerX + (contentWidth * 0.6f - artSize.Width) / 2;
|
|
||||||
g.DrawString(articleCode, dataFont, textBrush, artX, currentY + rowHeight/4);
|
|
||||||
currentY += rowHeight;
|
|
||||||
|
|
||||||
// Horizontal divider
|
|
||||||
g.DrawLine(dividerPen, contentX, currentY, contentX + contentWidth, currentY);
|
|
||||||
|
|
||||||
// Row 9: Prod Order
|
|
||||||
g.DrawString("Prod Order", labelFont, textBrush, contentX + 5, currentY + rowHeight/4);
|
|
||||||
string prodOrder = _labelData.SequentialNumber ?? $"{_labelData.ComandaProductie}-{_labelData.Cantitate}";
|
|
||||||
SizeF prodSize = g.MeasureString(prodOrder, dataFont);
|
|
||||||
float prodX = verticalDividerX + (contentWidth * 0.6f - prodSize.Width) / 2;
|
|
||||||
g.DrawString(prodOrder, dataFont, textBrush, prodX, currentY + rowHeight/4);
|
|
||||||
|
|
||||||
// Bottom barcode area
|
|
||||||
float barcodeY = contentY + contentHeight + (5f / 25.4f) * dpiY; // 5mm below content
|
|
||||||
float barcodeWidth = contentWidth;
|
|
||||||
float barcodeHeight = (10f / 25.4f) * dpiY; // 10mm height
|
|
||||||
|
|
||||||
// Draw barcode representation (simple lines)
|
|
||||||
DrawSimpleBarcode(g, contentX, barcodeY, barcodeWidth, barcodeHeight, prodOrder);
|
|
||||||
|
|
||||||
// Barcode text
|
|
||||||
SizeF barcodeTextSize = g.MeasureString(prodOrder, barcodeFont);
|
|
||||||
float barcodeTextX = contentX + (contentWidth - barcodeTextSize.Width) / 2;
|
|
||||||
g.DrawString(prodOrder, barcodeFont, textBrush, barcodeTextX, barcodeY + barcodeHeight + 5);
|
|
||||||
|
|
||||||
// Vertical barcode on the right side
|
|
||||||
float verticalBarcodeX = contentX + contentWidth + (5f / 25.4f) * dpiX; // 5mm to the right
|
|
||||||
float verticalBarcodeWidth = (8f / 25.4f) * dpiX; // 8mm width
|
|
||||||
DrawSimpleVerticalBarcode(g, verticalBarcodeX, contentY + rowHeight * 2,
|
|
||||||
verticalBarcodeWidth, contentHeight - rowHeight * 2, customerOrder);
|
|
||||||
|
|
||||||
// Dispose resources
|
|
||||||
companyFont.Dispose();
|
|
||||||
headerFont.Dispose();
|
|
||||||
dataFont.Dispose();
|
|
||||||
labelFont.Dispose();
|
|
||||||
barcodeFont.Dispose();
|
|
||||||
borderPen.Dispose();
|
|
||||||
dividerPen.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error in PrintPage event: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimpleBarcode(Graphics g, float x, float y, float width, float height, string data)
|
|
||||||
{
|
|
||||||
// Simple barcode representation using alternating lines
|
|
||||||
float barWidth = 2f;
|
|
||||||
bool drawBar = true;
|
|
||||||
|
|
||||||
for (float currentX = x; currentX < x + width; currentX += barWidth)
|
|
||||||
{
|
|
||||||
if (drawBar)
|
|
||||||
{
|
|
||||||
g.FillRectangle(Brushes.Black, currentX, y, barWidth, height * 0.8f);
|
|
||||||
}
|
|
||||||
drawBar = !drawBar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimpleVerticalBarcode(Graphics g, float x, float y, float width, float height, string data)
|
|
||||||
{
|
|
||||||
// Simple vertical barcode representation
|
|
||||||
float barHeight = 2f;
|
|
||||||
bool drawBar = true;
|
|
||||||
|
|
||||||
for (float currentY = y; currentY < y + height; currentY += barHeight)
|
|
||||||
{
|
|
||||||
if (drawBar)
|
|
||||||
{
|
|
||||||
g.FillRectangle(Brushes.Black, x, currentY, width * 0.8f, barHeight);
|
|
||||||
}
|
|
||||||
drawBar = !drawBar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace RecticelPrintService
|
|
||||||
{
|
|
||||||
public class PrintData
|
|
||||||
{
|
|
||||||
[JsonProperty("order_id")]
|
|
||||||
public int OrderId { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("comanda_productie")]
|
|
||||||
public string ComandaProductie { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("customer_name")]
|
|
||||||
public string CustomerName { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("cantitate")]
|
|
||||||
public int Cantitate { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("com_achiz_client")]
|
|
||||||
public string ComAchizClient { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("nr_linie_com_client")]
|
|
||||||
public string NrLinieComClient { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("data_livrare")]
|
|
||||||
public string DataLivrare { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("dimensiune")]
|
|
||||||
public string Dimensiune { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("descr_com_prod")]
|
|
||||||
public string DescrComProd { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("customer_article_number")]
|
|
||||||
public string CustomerArticleNumber { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("cod_articol")]
|
|
||||||
public string CodArticol { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sequential_number")]
|
|
||||||
public string SequentialNumber { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("current_label")]
|
|
||||||
public int CurrentLabel { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("total_labels")]
|
|
||||||
public int TotalLabels { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Web;
|
|
||||||
|
|
||||||
namespace RecticelPrintService
|
|
||||||
{
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (args.Length == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Recticel Print Service - No arguments provided");
|
|
||||||
Console.WriteLine("Usage: RecticelPrintService.exe \"recticel-print://printer/data\"");
|
|
||||||
Console.ReadKey();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string url = args[0];
|
|
||||||
Console.WriteLine($"Received print request: {url}");
|
|
||||||
|
|
||||||
// Parse the custom protocol URL
|
|
||||||
// Format: recticel-print://printer_name/base64_encoded_json_data
|
|
||||||
if (!url.StartsWith("recticel-print://"))
|
|
||||||
{
|
|
||||||
Console.WriteLine("Invalid protocol. Expected: recticel-print://");
|
|
||||||
Console.ReadKey();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove protocol prefix
|
|
||||||
string data = url.Substring("recticel-print://".Length);
|
|
||||||
|
|
||||||
// Split printer name and data
|
|
||||||
string[] parts = data.Split('/');
|
|
||||||
if (parts.Length < 2)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Invalid URL format. Expected: recticel-print://printer_name/data");
|
|
||||||
Console.ReadKey();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string printerName = HttpUtility.UrlDecode(parts[0]);
|
|
||||||
string encodedData = HttpUtility.UrlDecode(parts[1]);
|
|
||||||
|
|
||||||
Console.WriteLine($"Target Printer: {printerName}");
|
|
||||||
Console.WriteLine($"Data Length: {encodedData.Length}");
|
|
||||||
|
|
||||||
// Decode base64 JSON data
|
|
||||||
byte[] dataBytes = Convert.FromBase64String(encodedData);
|
|
||||||
string jsonData = System.Text.Encoding.UTF8.GetString(dataBytes);
|
|
||||||
|
|
||||||
Console.WriteLine($"Decoded JSON: {jsonData}");
|
|
||||||
|
|
||||||
// Parse and print the label
|
|
||||||
var printer = new LabelPrinter();
|
|
||||||
bool success = printer.PrintLabel(printerName, jsonData);
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
Console.WriteLine("✅ Label printed successfully!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("❌ Failed to print label!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep console open for debugging (remove in production)
|
|
||||||
Console.WriteLine("Press any key to exit...");
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error: {ex.Message}");
|
|
||||||
Console.WriteLine(ex.StackTrace);
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
[assembly: AssemblyTitle("RecticelPrintService")]
|
|
||||||
[assembly: AssemblyDescription("Direct label printing service for Recticel Quality System")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("Recticel")]
|
|
||||||
[assembly: AssemblyProduct("Recticel Print Service")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © Recticel 2025")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
[assembly: Guid("41ed6235-3c76-4723-a5f6-02d2fa51e677")]
|
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{41ED6235-3C76-4723-A5F6-02D2FA51E677}</ProjectGuid>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<RootNamespace>RecticelPrintService</RootNamespace>
|
|
||||||
<AssemblyName>RecticelPrintService</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
|
||||||
<Deterministic>true</Deterministic>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Drawing" />
|
|
||||||
<Reference Include="System.Drawing.Printing" />
|
|
||||||
<Reference Include="System.Web" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
<Reference Include="Newtonsoft.Json">
|
|
||||||
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Program.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="LabelPrinter.cs" />
|
|
||||||
<Compile Include="PrintData.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
<None Include="packages.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
</Project>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<packages>
|
|
||||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
|
||||||
</packages>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo Installing Recticel Print Service...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check if running as Administrator
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ERROR: This script must be run as Administrator.
|
|
||||||
echo Right-click and select "Run as administrator"
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Create installation directory
|
|
||||||
echo Creating installation directory...
|
|
||||||
if not exist "C:\RecticelPrintService" (
|
|
||||||
mkdir "C:\RecticelPrintService"
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Copy files (assuming this script is in the same directory as the built application)
|
|
||||||
echo Copying application files...
|
|
||||||
copy "RecticelPrintService\bin\Release\*.*" "C:\RecticelPrintService\" /Y
|
|
||||||
|
|
||||||
REM Register custom protocol
|
|
||||||
echo Registering custom protocol...
|
|
||||||
reg import "recticel-print-protocol.reg"
|
|
||||||
|
|
||||||
if %errorlevel% equ 0 (
|
|
||||||
echo.
|
|
||||||
echo ✅ Installation completed successfully!
|
|
||||||
echo.
|
|
||||||
echo Service installed to: C:\RecticelPrintService\
|
|
||||||
echo Protocol registered: recticel-print://
|
|
||||||
echo.
|
|
||||||
echo Next steps:
|
|
||||||
echo 1. Configure your thermal label printer in Windows
|
|
||||||
echo 2. Set label size to 80mm x 110mm
|
|
||||||
echo 3. Test printing from the web interface
|
|
||||||
echo.
|
|
||||||
echo Press any key to exit...
|
|
||||||
pause >nul
|
|
||||||
) else (
|
|
||||||
echo.
|
|
||||||
echo ❌ Installation failed!
|
|
||||||
echo Please check if you're running as Administrator.
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
)
|
|
||||||
|
|
||||||
exit /b 0
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
Windows Registry Editor Version 5.00
|
|
||||||
|
|
||||||
[HKEY_CLASSES_ROOT\recticel-print]
|
|
||||||
@="URL:Recticel Print Protocol"
|
|
||||||
"URL Protocol"=""
|
|
||||||
"EditFlags"=dword:00000002
|
|
||||||
|
|
||||||
[HKEY_CLASSES_ROOT\recticel-print\shell]
|
|
||||||
@="open"
|
|
||||||
|
|
||||||
[HKEY_CLASSES_ROOT\recticel-print\shell\open]
|
|
||||||
|
|
||||||
[HKEY_CLASSES_ROOT\recticel-print\shell\open\command]
|
|
||||||
@="\"C:\\RecticelPrintService\\RecticelPrintService.exe\" \"%1\""
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
# Windows Service Error 1053 - COMPLETE FIX PACKAGE
|
|
||||||
|
|
||||||
## 🎯 Problem Description
|
|
||||||
|
|
||||||
**Windows Service Error 1053**: "The service did not respond to the start or control request in a timely fashion."
|
|
||||||
|
|
||||||
This error occurs when:
|
|
||||||
- Service takes too long to respond to Windows Service Control Manager (SCM)
|
|
||||||
- Python process doesn't communicate properly with Windows services
|
|
||||||
- Service wrapper doesn't handle SCM signals correctly
|
|
||||||
- Dependencies or paths are incorrect
|
|
||||||
|
|
||||||
## 📦 Complete Solution Package
|
|
||||||
|
|
||||||
This package provides **4 different installation methods** and comprehensive Error 1053 fixes:
|
|
||||||
|
|
||||||
### 🔧 Installation Files
|
|
||||||
|
|
||||||
1. **`install_service_ENHANCED.bat`** - Main installer with multiple fallback methods
|
|
||||||
2. **`fix_error_1053.bat`** - Dedicated Error 1053 diagnostic and fix tool
|
|
||||||
3. **`print_service_complete.py`** - Enhanced service with proper Windows service support
|
|
||||||
4. **`service_wrapper.py`** - Optional advanced service wrapper
|
|
||||||
5. **`test_service.bat`** - Standalone testing tool
|
|
||||||
|
|
||||||
### 🚀 Installation Methods (Automatic Fallback)
|
|
||||||
|
|
||||||
#### Method 1: Windows SC Service (Preferred)
|
|
||||||
- Creates standard Windows service
|
|
||||||
- Includes enhanced timeout handling
|
|
||||||
- Automatic recovery configuration
|
|
||||||
- **Fixes Error 1053** with proper SCM communication
|
|
||||||
|
|
||||||
#### Method 2: Task Scheduler Service (Fallback)
|
|
||||||
- Runs as scheduled task on system startup
|
|
||||||
- SYSTEM privileges with highest elevation
|
|
||||||
- Automatic restart on failure
|
|
||||||
- Bypasses SCM timeout issues
|
|
||||||
|
|
||||||
#### Method 3: Startup Script (Manual Fallback)
|
|
||||||
- Runs from Windows startup folder
|
|
||||||
- Simple and reliable
|
|
||||||
- Manual process management
|
|
||||||
- Always works as final resort
|
|
||||||
|
|
||||||
#### Method 4: Standalone Mode (Testing/Debugging)
|
|
||||||
- Direct Python execution
|
|
||||||
- No Windows service wrapper
|
|
||||||
- Immediate startup for testing
|
|
||||||
- Perfect for troubleshooting
|
|
||||||
|
|
||||||
## 🔍 Error 1053 Specific Fixes
|
|
||||||
|
|
||||||
### Root Cause Analysis
|
|
||||||
The enhanced installers address these Error 1053 causes:
|
|
||||||
|
|
||||||
1. **SCM Timeout Issues**
|
|
||||||
- Enhanced service wrapper responds immediately to SCM
|
|
||||||
- Service process starts in background
|
|
||||||
- Proper exit codes and signaling
|
|
||||||
|
|
||||||
2. **Python Path Problems**
|
|
||||||
- Automatic detection of embedded Python
|
|
||||||
- Fallback to system Python
|
|
||||||
- Absolute path resolution
|
|
||||||
|
|
||||||
3. **Service Communication**
|
|
||||||
- Proper Windows service signal handling
|
|
||||||
- Enhanced logging and error reporting
|
|
||||||
- Background process management
|
|
||||||
|
|
||||||
4. **Dependency Issues**
|
|
||||||
- Self-contained Python environment
|
|
||||||
- Zero external dependencies
|
|
||||||
- Embedded Python distribution included
|
|
||||||
|
|
||||||
### Technical Implementation
|
|
||||||
|
|
||||||
#### Enhanced Service Wrapper
|
|
||||||
```batch
|
|
||||||
# error_1053_fix_wrapper.bat
|
|
||||||
- Immediate SCM response
|
|
||||||
- Background service startup
|
|
||||||
- Enhanced error handling
|
|
||||||
- Comprehensive logging
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Service Configuration
|
|
||||||
```cmd
|
|
||||||
# Delayed auto-start to prevent startup conflicts
|
|
||||||
sc config QualityPrintService start= delayed-auto
|
|
||||||
|
|
||||||
# Automatic recovery on failure
|
|
||||||
sc failure QualityPrintService reset= 86400 actions= restart/5000/restart/5000/restart/5000
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 Usage Instructions
|
|
||||||
|
|
||||||
### 🎯 Quick Fix (Recommended)
|
|
||||||
1. **Run as Administrator**: `fix_error_1053.bat`
|
|
||||||
2. This script will:
|
|
||||||
- Diagnose the current problem
|
|
||||||
- Apply all Error 1053 fixes
|
|
||||||
- Test service functionality
|
|
||||||
- Install with enhanced wrapper
|
|
||||||
|
|
||||||
### 🔧 Fresh Installation
|
|
||||||
1. **Run as Administrator**: `install_service_ENHANCED.bat`
|
|
||||||
2. Installer will automatically:
|
|
||||||
- Try Windows SC Service first
|
|
||||||
- Fall back to Task Scheduler if needed
|
|
||||||
- Create startup script as final option
|
|
||||||
- Test all methods until one succeeds
|
|
||||||
|
|
||||||
### 🧪 Testing and Verification
|
|
||||||
1. **Test standalone**: `test_service.bat`
|
|
||||||
2. **Manual testing**:
|
|
||||||
```cmd
|
|
||||||
cd C:\QualityPrintService
|
|
||||||
python print_service_complete.py --test
|
|
||||||
python print_service_complete.py --standalone
|
|
||||||
```
|
|
||||||
3. **Service status**:
|
|
||||||
```cmd
|
|
||||||
sc query QualityPrintService
|
|
||||||
net start QualityPrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔍 Troubleshooting Guide
|
|
||||||
|
|
||||||
### If Error 1053 Still Occurs
|
|
||||||
|
|
||||||
1. **Run Diagnostic Tool**:
|
|
||||||
```cmd
|
|
||||||
fix_error_1053.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Check Logs**:
|
|
||||||
- `%USERPROFILE%\PrintService\logs\service_wrapper.log`
|
|
||||||
- `%USERPROFILE%\PrintService\logs\error_1053_fix.log`
|
|
||||||
|
|
||||||
3. **Manual Service Test**:
|
|
||||||
```cmd
|
|
||||||
C:\QualityPrintService\error_1053_fix_wrapper.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Alternative Installation**:
|
|
||||||
- Use Task Scheduler method
|
|
||||||
- Use Startup Script method
|
|
||||||
- Run in standalone mode
|
|
||||||
|
|
||||||
### Common Issues and Solutions
|
|
||||||
|
|
||||||
| Issue | Solution |
|
|
||||||
|-------|----------|
|
|
||||||
| "Python not found" | Use embedded Python package or install Python 3.7+ |
|
|
||||||
| "Port 8765 in use" | Stop existing services, reboot system |
|
|
||||||
| "Access denied" | Run installer as Administrator |
|
|
||||||
| "Service won't start" | Use Task Scheduler fallback method |
|
|
||||||
| "Still Error 1053" | Use startup script or standalone mode |
|
|
||||||
|
|
||||||
## ✅ Success Verification
|
|
||||||
|
|
||||||
After installation, verify success:
|
|
||||||
|
|
||||||
1. **Service Status**: Service should be "RUNNING"
|
|
||||||
```cmd
|
|
||||||
sc query QualityPrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Network Test**: Should return "OK"
|
|
||||||
```cmd
|
|
||||||
curl http://localhost:8765/health
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Browser Test**: Open `http://localhost:8765/health`
|
|
||||||
|
|
||||||
4. **Chrome Extension**: Should connect successfully
|
|
||||||
|
|
||||||
## 📊 Package Contents Summary
|
|
||||||
|
|
||||||
### Core Service Files
|
|
||||||
- ✅ `print_service_complete.py` - Main service with Windows service support
|
|
||||||
- ✅ `service_wrapper.py` - Advanced service wrapper (optional)
|
|
||||||
- ✅ Enhanced service wrappers with Error 1053 fixes
|
|
||||||
|
|
||||||
### Installation Tools
|
|
||||||
- ✅ `install_service_ENHANCED.bat` - Multi-method installer
|
|
||||||
- ✅ `fix_error_1053.bat` - Dedicated Error 1053 fixer
|
|
||||||
- ✅ `test_service.bat` - Standalone testing tool
|
|
||||||
|
|
||||||
### Zero Dependencies
|
|
||||||
- ✅ `python_embedded/` - Complete Python 3.11.9 distribution
|
|
||||||
- ✅ No external dependencies required
|
|
||||||
- ✅ Self-contained package (10.8MB)
|
|
||||||
|
|
||||||
### Browser Integration
|
|
||||||
- ✅ `chrome_extension/` - Complete Chrome extension
|
|
||||||
- ✅ Automatic printer detection
|
|
||||||
- ✅ PDF processing and printing
|
|
||||||
|
|
||||||
## 🎯 Expected Results
|
|
||||||
|
|
||||||
After running the enhanced installer:
|
|
||||||
|
|
||||||
✅ **Windows Service Error 1053 RESOLVED**
|
|
||||||
✅ **Service starts automatically on boot**
|
|
||||||
✅ **Multiple installation methods available**
|
|
||||||
✅ **Comprehensive error handling and recovery**
|
|
||||||
✅ **Zero external dependencies**
|
|
||||||
✅ **Complete diagnostic and troubleshooting tools**
|
|
||||||
|
|
||||||
## 🚀 Next Steps
|
|
||||||
|
|
||||||
1. **Install**: Run `install_service_ENHANCED.bat` as Administrator
|
|
||||||
2. **Verify**: Check service status and network connectivity
|
|
||||||
3. **Configure**: Install Chrome extension from `chrome_extension/`
|
|
||||||
4. **Test**: Print labels from web application
|
|
||||||
5. **Monitor**: Check logs for any issues
|
|
||||||
|
|
||||||
The enhanced package provides **4 installation methods** and **comprehensive Error 1053 fixes** to ensure reliable service operation on all Windows systems.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Support**: All installation methods include detailed logging and diagnostic information to troubleshoot any remaining issues.
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
# Quality Print Service - Complete Installation Guide
|
|
||||||
|
|
||||||
## 🎯 Pre-Installation Checklist
|
|
||||||
|
|
||||||
### System Requirements Verification:
|
|
||||||
- [ ] Windows 10 build 1903+ or Windows Server 2016+
|
|
||||||
- [ ] Administrator access to the system
|
|
||||||
- [ ] Google Chrome browser installed
|
|
||||||
- [ ] At least 100 MB free disk space
|
|
||||||
- [ ] Network/USB printer connected and configured
|
|
||||||
|
|
||||||
### Python Requirements:
|
|
||||||
- [ ] Python 3.7+ installed OR use included portable Python
|
|
||||||
- [ ] Python accessible via command line (optional)
|
|
||||||
|
|
||||||
## 📦 Installation Methods
|
|
||||||
|
|
||||||
### Method 1: Complete Automatic Installation (Recommended)
|
|
||||||
|
|
||||||
1. **Download and Extract**:
|
|
||||||
- Extract the complete package to any folder (e.g., Desktop)
|
|
||||||
- No need to keep the files permanently
|
|
||||||
|
|
||||||
2. **Run Installer**:
|
|
||||||
```
|
|
||||||
Right-click: install_service_complete.bat
|
|
||||||
Select: "Run as administrator"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Follow Installation Steps**:
|
|
||||||
```
|
|
||||||
[1/6] Administrator privileges confirmed ✓
|
|
||||||
[2/6] Checking Python installation...
|
|
||||||
[3/6] Creating installation directories...
|
|
||||||
[4/6] Installing service files...
|
|
||||||
[5/6] Installing Windows service...
|
|
||||||
[6/6] Starting service...
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Install Chrome Extension**:
|
|
||||||
- Chrome will open the extension folder automatically
|
|
||||||
- Go to `chrome://extensions/`
|
|
||||||
- Enable "Developer mode" (top right)
|
|
||||||
- Click "Load unpacked"
|
|
||||||
- Select the `chrome_extension` folder
|
|
||||||
|
|
||||||
5. **Verify Installation**:
|
|
||||||
- Visit: `http://localhost:8765/health`
|
|
||||||
- Expected response: `{"status": "healthy"}`
|
|
||||||
|
|
||||||
### Method 2: Manual Installation
|
|
||||||
|
|
||||||
1. **Create Directories**:
|
|
||||||
```cmd
|
|
||||||
mkdir C:\QualityPrintService
|
|
||||||
mkdir %USERPROFILE%\PrintService\logs
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Copy Files**:
|
|
||||||
```cmd
|
|
||||||
copy print_service_complete.py C:\QualityPrintService ```
|
|
||||||
|
|
||||||
3. **Install Service**:
|
|
||||||
```cmd
|
|
||||||
sc create QualityPrintService binPath="python C:\QualityPrintService\print_service_complete.py"
|
|
||||||
sc start QualityPrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Post-Installation Configuration
|
|
||||||
|
|
||||||
### Service Verification:
|
|
||||||
```cmd
|
|
||||||
# Check service status
|
|
||||||
sc query QualityPrintService
|
|
||||||
|
|
||||||
# Check service configuration
|
|
||||||
sc qc QualityPrintService
|
|
||||||
|
|
||||||
# View service logs (if using NSSM)
|
|
||||||
type "%USERPROFILE%\PrintService\logs\service_output.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Network Testing:
|
|
||||||
```powershell
|
|
||||||
# Test health endpoint
|
|
||||||
Invoke-RestMethod -Uri http://localhost:8765/health
|
|
||||||
|
|
||||||
# Test printer endpoint
|
|
||||||
Invoke-RestMethod -Uri http://localhost:8765/printers
|
|
||||||
|
|
||||||
# Test from browser
|
|
||||||
start http://localhost:8765/status
|
|
||||||
```
|
|
||||||
|
|
||||||
### Chrome Extension Setup:
|
|
||||||
1. Open Chrome browser
|
|
||||||
2. Navigate to `chrome://extensions/`
|
|
||||||
3. Enable "Developer mode" toggle (top-right corner)
|
|
||||||
4. Click "Load unpacked" button
|
|
||||||
5. Browse and select the `chrome_extension` folder
|
|
||||||
6. Verify extension appears in the list with green toggle
|
|
||||||
|
|
||||||
## 🔍 Installation Verification
|
|
||||||
|
|
||||||
### Health Check Procedure:
|
|
||||||
1. **Service Status**: Verify Windows service is running
|
|
||||||
```cmd
|
|
||||||
sc query QualityPrintService | find "RUNNING"
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Network Connectivity**: Test HTTP endpoints
|
|
||||||
```cmd
|
|
||||||
curl http://localhost:8765/health
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Printer Detection**: Check printer enumeration
|
|
||||||
```cmd
|
|
||||||
curl http://localhost:8765/printers
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Extension Communication**: Test from web page
|
|
||||||
- Open the Quality app in Chrome
|
|
||||||
- Go to print module
|
|
||||||
- Verify "Extension ready" status
|
|
||||||
|
|
||||||
### Expected Responses:
|
|
||||||
|
|
||||||
**Health Check**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "healthy",
|
|
||||||
"service": "Windows Print Service",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"timestamp": "2025-09-25T10:30:00"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Printer List**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"printers": [
|
|
||||||
{"name": "HP LaserJet", "type": "Local", "status": "Available"}
|
|
||||||
],
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚨 Troubleshooting Common Issues
|
|
||||||
|
|
||||||
### Issue: "Administrator privileges required"
|
|
||||||
**Solution**:
|
|
||||||
- Right-click installer file
|
|
||||||
- Select "Run as administrator"
|
|
||||||
- Confirm UAC prompt
|
|
||||||
|
|
||||||
### Issue: "Python not found"
|
|
||||||
**Solutions**:
|
|
||||||
1. Install Python from python.org
|
|
||||||
2. Use included portable Python
|
|
||||||
3. Add Python to system PATH
|
|
||||||
|
|
||||||
### Issue: "Service failed to start"
|
|
||||||
**Solutions**:
|
|
||||||
1. Check Windows Event Viewer:
|
|
||||||
- Windows Logs → Application
|
|
||||||
- Filter by source: "Service Control Manager"
|
|
||||||
2. Verify port 8765 is not in use:
|
|
||||||
```cmd
|
|
||||||
netstat -an | find "8765"
|
|
||||||
```
|
|
||||||
3. Check service logs:
|
|
||||||
```cmd
|
|
||||||
type "%USERPROFILE%\PrintService\logs\print_service_*.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue: "Chrome extension not working"
|
|
||||||
**Solutions**:
|
|
||||||
1. Reload extension in `chrome://extensions/`
|
|
||||||
2. Check extension permissions
|
|
||||||
3. Verify service is responding at `localhost:8765`
|
|
||||||
4. Clear browser cache and cookies
|
|
||||||
|
|
||||||
### Issue: "PDF printing fails"
|
|
||||||
**Solutions**:
|
|
||||||
1. Install Adobe Reader or SumatraPDF
|
|
||||||
2. Check printer permissions
|
|
||||||
3. Verify PDF file accessibility
|
|
||||||
4. Test with different printer
|
|
||||||
|
|
||||||
## 🔄 Maintenance and Updates
|
|
||||||
|
|
||||||
### Regular Maintenance:
|
|
||||||
- **Log Cleanup**: Logs rotate automatically
|
|
||||||
- **Service Monitoring**: Check service status weekly
|
|
||||||
- **Chrome Extension**: Update when prompted
|
|
||||||
|
|
||||||
### Manual Service Management:
|
|
||||||
```cmd
|
|
||||||
# Stop service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
|
|
||||||
# Start service
|
|
||||||
sc start QualityPrintService
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
sc stop QualityPrintService && timeout /t 3 && sc start QualityPrintService
|
|
||||||
|
|
||||||
# Change startup type
|
|
||||||
sc config QualityPrintService start= auto
|
|
||||||
```
|
|
||||||
|
|
||||||
### Log File Locations:
|
|
||||||
- Service logs: `%USERPROFILE%\PrintService\logs\`
|
|
||||||
- Windows Event Logs: Event Viewer → Windows Logs → Application
|
|
||||||
- Chrome Extension: Chrome DevTools → Console
|
|
||||||
|
|
||||||
## 🔧 Advanced Configuration
|
|
||||||
|
|
||||||
### Custom Port Configuration:
|
|
||||||
Edit `print_service_complete.py`:
|
|
||||||
```python
|
|
||||||
server_address = ('localhost', 8765) # Change 8765 to desired port
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Install Directory:
|
|
||||||
Edit `install_service_complete.bat`:
|
|
||||||
```batch
|
|
||||||
set INSTALL_DIR=C:\CustomPath\PrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Recovery Options:
|
|
||||||
```cmd
|
|
||||||
sc failure QualityPrintService reset= 86400 actions= restart/5000/restart/10000/restart/30000
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 Uninstallation
|
|
||||||
|
|
||||||
### Complete Removal:
|
|
||||||
1. Run `uninstall_service_complete.bat` as Administrator
|
|
||||||
2. Remove Chrome extension manually
|
|
||||||
3. Optional: Delete log files
|
|
||||||
|
|
||||||
### Manual Removal:
|
|
||||||
```cmd
|
|
||||||
# Stop and remove service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
sc delete QualityPrintService
|
|
||||||
|
|
||||||
# Remove files
|
|
||||||
rmdir /s /q C:\QualityPrintService
|
|
||||||
rmdir /s /q "%USERPROFILE%\PrintService"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📞 Getting Help
|
|
||||||
|
|
||||||
### Before Contacting Support:
|
|
||||||
1. Check this installation guide
|
|
||||||
2. Review troubleshooting section
|
|
||||||
3. Check service logs for error messages
|
|
||||||
4. Test with simple printer (like Microsoft Print to PDF)
|
|
||||||
|
|
||||||
### Information to Provide:
|
|
||||||
- Windows version (run `winver`)
|
|
||||||
- Python version (run `python --version`)
|
|
||||||
- Service status (run `sc query QualityPrintService`)
|
|
||||||
- Recent log entries
|
|
||||||
- Error messages or screenshots
|
|
||||||
|
|
||||||
---
|
|
||||||
**Installation Guide Version**: 1.0.0
|
|
||||||
**Last Updated**: September 2025
|
|
||||||
**Support**: Internal Quality System Team
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
# Quality Windows Print Service - Complete Self-Contained Package Summary
|
|
||||||
|
|
||||||
## 📦 Package Overview
|
|
||||||
|
|
||||||
This is a **complete, self-contained Windows print service** with **zero external dependencies** that enables silent PDF printing from the Quality Label web application.
|
|
||||||
|
|
||||||
## 🎯 Key Features
|
|
||||||
|
|
||||||
### ✅ Zero Dependencies
|
|
||||||
- Uses **only Python standard library** (no external packages required)
|
|
||||||
- Works with **any Python 3.7+** installation (system or portable)
|
|
||||||
- No complicated setup or package management
|
|
||||||
|
|
||||||
### ✅ Complete Windows Integration
|
|
||||||
- **Windows Service** with automatic startup
|
|
||||||
- **Service recovery** with auto-restart on failure
|
|
||||||
- **Professional logging** with daily rotation
|
|
||||||
- **Chrome extension** for seamless web integration
|
|
||||||
|
|
||||||
### ✅ Multiple Printing Methods (Automatic Fallback)
|
|
||||||
1. **Adobe Reader** command line printing
|
|
||||||
2. **SumatraPDF** automation
|
|
||||||
3. **PowerShell** printing commands
|
|
||||||
4. **Microsoft Edge** integration
|
|
||||||
5. **Windows system default** application
|
|
||||||
|
|
||||||
### ✅ Robust Architecture
|
|
||||||
```
|
|
||||||
Quality Web App → Chrome Extension → Windows Service → Physical Printer
|
|
||||||
(localhost only) (port 8765) (any printer)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 Package Contents
|
|
||||||
|
|
||||||
### Core Service Files:
|
|
||||||
- `print_service_complete.py` - Main service (19KB, pure Python)
|
|
||||||
- `install_service_complete.bat` - Complete installer with Python detection
|
|
||||||
- `uninstall_service_complete.bat` - Clean removal script
|
|
||||||
- `requirements_complete.txt` - Dependencies list (all standard library)
|
|
||||||
|
|
||||||
### Chrome Extension:
|
|
||||||
- `chrome_extension/manifest.json` - Extension configuration
|
|
||||||
- `chrome_extension/background.js` - Windows service communication
|
|
||||||
- `chrome_extension/popup.html` - Extension interface
|
|
||||||
- `chrome_extension/popup.js` - Extension functionality
|
|
||||||
|
|
||||||
### Documentation:
|
|
||||||
- `README_COMPLETE.md` - Comprehensive technical documentation
|
|
||||||
- `INSTALLATION_COMPLETE.md` - Step-by-step installation guide
|
|
||||||
- `PORTABLE_PYTHON_INSTRUCTIONS.txt` - Python distribution options
|
|
||||||
|
|
||||||
### Build Tools:
|
|
||||||
- `build_package.py` - Package builder and documentation generator
|
|
||||||
- `build_executable.bat` - Optional standalone .exe builder
|
|
||||||
|
|
||||||
## 🚀 Installation Process (5 Minutes)
|
|
||||||
|
|
||||||
### Prerequisites:
|
|
||||||
- Windows 10/11 or Windows Server 2016+
|
|
||||||
- Administrator privileges
|
|
||||||
- Google Chrome browser
|
|
||||||
- Python 3.7+ (system or portable)
|
|
||||||
|
|
||||||
### Steps:
|
|
||||||
1. **Extract Package** - Extract ZIP to any temporary location
|
|
||||||
2. **Run Installer** - Right-click `install_service_complete.bat` → "Run as administrator"
|
|
||||||
3. **Install Extension** - Load `chrome_extension` folder in Chrome
|
|
||||||
4. **Test Service** - Visit http://localhost:8765/health
|
|
||||||
|
|
||||||
## 🔧 Technical Specifications
|
|
||||||
|
|
||||||
### Service Details:
|
|
||||||
- **Port**: 8765 (localhost only for security)
|
|
||||||
- **Memory Usage**: ~15-30 MB
|
|
||||||
- **CPU Usage**: <1% (idle)
|
|
||||||
- **Installation Path**: `C:\QualityPrintService\`
|
|
||||||
- **Logs**: `%USERPROFILE%\PrintService\logs\`
|
|
||||||
|
|
||||||
### API Endpoints:
|
|
||||||
- `GET /health` - Service health check and status
|
|
||||||
- `GET /printers` - List available system printers
|
|
||||||
- `GET /status` - Service statistics and uptime
|
|
||||||
- `POST /print_pdf` - Print PDF file (supports page-by-page)
|
|
||||||
|
|
||||||
### Printing Workflow:
|
|
||||||
1. User selects order in Quality web app
|
|
||||||
2. User clicks "Print Labels"
|
|
||||||
3. Chrome extension detects Windows service
|
|
||||||
4. PDF is generated and sent to service
|
|
||||||
5. Service downloads PDF and prints using best available method
|
|
||||||
6. Temporary files are automatically cleaned up
|
|
||||||
|
|
||||||
## 🛡️ Security & Reliability
|
|
||||||
|
|
||||||
### Security Features:
|
|
||||||
- **Localhost only** - Service only accepts connections from 127.0.0.1
|
|
||||||
- **No external network** - All communication is local
|
|
||||||
- **Secure PDF handling** - Temporary files with auto-cleanup
|
|
||||||
- **Extension origin validation** - Chrome extension verifies sender origins
|
|
||||||
|
|
||||||
### Reliability Features:
|
|
||||||
- **Service recovery** - Auto-restart on failure (3 attempts)
|
|
||||||
- **Multiple print methods** - Automatic fallback if one method fails
|
|
||||||
- **Error logging** - Comprehensive error tracking and debugging
|
|
||||||
- **Health monitoring** - Built-in health checks and status reporting
|
|
||||||
|
|
||||||
## 📈 Advantages Over Previous Approaches
|
|
||||||
|
|
||||||
### vs Chrome-Only Printing:
|
|
||||||
- ✅ **No browser security restrictions**
|
|
||||||
- ✅ **True silent printing** (no print dialogs)
|
|
||||||
- ✅ **Reliable printer selection**
|
|
||||||
- ✅ **Page-by-page printing support**
|
|
||||||
|
|
||||||
### vs PowerShell-Only Service:
|
|
||||||
- ✅ **More portable** (works without PowerShell expertise)
|
|
||||||
- ✅ **Better error handling** and logging
|
|
||||||
- ✅ **More printing methods** available
|
|
||||||
- ✅ **Easier debugging** and maintenance
|
|
||||||
|
|
||||||
### vs External Dependencies:
|
|
||||||
- ✅ **Zero package management** complexity
|
|
||||||
- ✅ **No version conflicts** or update issues
|
|
||||||
- ✅ **Works on restricted systems**
|
|
||||||
- ✅ **Smaller footprint** and faster startup
|
|
||||||
|
|
||||||
## 🎯 Why This Solution Works
|
|
||||||
|
|
||||||
### For IT Administrators:
|
|
||||||
- **Simple deployment** - One ZIP file, one installer command
|
|
||||||
- **Professional service** - Proper Windows service with logging
|
|
||||||
- **Easy maintenance** - Self-contained with automatic recovery
|
|
||||||
- **Security compliant** - Localhost only, no external dependencies
|
|
||||||
|
|
||||||
### For End Users:
|
|
||||||
- **Transparent operation** - Just click print, labels appear
|
|
||||||
- **No manual steps** - No print dialogs or file management
|
|
||||||
- **Reliable printing** - Multiple fallback methods ensure success
|
|
||||||
- **Professional interface** - Clean integration with Quality app
|
|
||||||
|
|
||||||
### For Developers:
|
|
||||||
- **Clean architecture** - Clear separation of concerns
|
|
||||||
- **Extensible design** - Easy to add new printing methods
|
|
||||||
- **Comprehensive logging** - Full debugging and monitoring
|
|
||||||
- **Standard protocols** - HTTP API for easy integration
|
|
||||||
|
|
||||||
## 🔮 Future Enhancements
|
|
||||||
|
|
||||||
### Planned Features:
|
|
||||||
- **Print queue management** - View and manage pending jobs
|
|
||||||
- **Printer status monitoring** - Real-time printer health checks
|
|
||||||
- **Print job history** - Track completed and failed jobs
|
|
||||||
- **Configuration GUI** - Windows app for service settings
|
|
||||||
|
|
||||||
### Optional Add-ons:
|
|
||||||
- **Standalone .exe** - Single file deployment (using PyInstaller)
|
|
||||||
- **MSI installer** - Enterprise deployment package
|
|
||||||
- **Network printing** - Remote printer support
|
|
||||||
- **Print templates** - Custom label formats
|
|
||||||
|
|
||||||
## 📊 Installation Statistics
|
|
||||||
|
|
||||||
### Installation Time: **~5 minutes**
|
|
||||||
- Package extraction: 30 seconds
|
|
||||||
- Service installation: 2 minutes
|
|
||||||
- Chrome extension: 1 minute
|
|
||||||
- Testing and verification: 1.5 minutes
|
|
||||||
|
|
||||||
### Maintenance Required: **Zero**
|
|
||||||
- Auto-starts with Windows
|
|
||||||
- Self-recovery on failure
|
|
||||||
- Automatic log rotation
|
|
||||||
- No user intervention needed
|
|
||||||
|
|
||||||
## 🎉 Ready for Production
|
|
||||||
|
|
||||||
This package is **production-ready** and includes:
|
|
||||||
- ✅ Complete installation automation
|
|
||||||
- ✅ Professional error handling
|
|
||||||
- ✅ Comprehensive documentation
|
|
||||||
- ✅ Testing and verification tools
|
|
||||||
- ✅ Clean uninstallation process
|
|
||||||
|
|
||||||
**The Quality Windows Print Service provides enterprise-grade label printing with consumer-friendly simplicity.**
|
|
||||||
|
|
||||||
---
|
|
||||||
**Package Version**: 1.0.0 Complete
|
|
||||||
**Build Date**: September 25, 2025
|
|
||||||
**Python Compatibility**: 3.7+ (all versions)
|
|
||||||
**Windows Compatibility**: Windows 10+ / Server 2016+
|
|
||||||
**Chrome Compatibility**: All recent versions
|
|
||||||
|
|
||||||
**Status**: ✅ Production Ready - Zero Dependencies - Self Contained
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
# PORTABLE PYTHON PACKAGE INSTRUCTIONS
|
|
||||||
|
|
||||||
To create a complete self-contained package, you need to include a portable Python interpreter.
|
|
||||||
|
|
||||||
## Option 1: Download Embedded Python (Recommended)
|
|
||||||
1. Download Python 3.11 Embedded from: https://www.python.org/downloads/windows/
|
|
||||||
2. Choose "Windows embeddable package (64-bit)" or "(32-bit)"
|
|
||||||
3. Extract to a folder named 'python_portable'
|
|
||||||
4. The structure should be:
|
|
||||||
python_portable/
|
|
||||||
├── python.exe
|
|
||||||
├── python311.dll (or similar)
|
|
||||||
├── pythoncom311.dll
|
|
||||||
└── ... (other Python files)
|
|
||||||
|
|
||||||
## Option 2: Use PyInstaller (Alternative)
|
|
||||||
1. Install PyInstaller: pip install pyinstaller
|
|
||||||
2. Run: pyinstaller --onefile --noconsole print_service_complete.py
|
|
||||||
3. This creates a single .exe file with all dependencies
|
|
||||||
|
|
||||||
## Option 3: Manual Python Installation Check
|
|
||||||
The installer will check for system Python and use it if available.
|
|
||||||
|
|
||||||
## Current Package Structure
|
|
||||||
This package includes:
|
|
||||||
✓ Complete Python script with all dependencies
|
|
||||||
✓ Windows service installer
|
|
||||||
✓ Chrome extension
|
|
||||||
✓ Logging and error handling
|
|
||||||
✓ Multiple printing method fallbacks
|
|
||||||
✓ Automatic recovery options
|
|
||||||
|
|
||||||
## Dependencies Included in Python Script:
|
|
||||||
- All standard library modules (http.server, json, subprocess, etc.)
|
|
||||||
- No external dependencies required
|
|
||||||
- Pure Python implementation
|
|
||||||
|
|
||||||
The service will work with any Python 3.7+ installation.
|
|
||||||
Binary file not shown.
@@ -1,167 +0,0 @@
|
|||||||
# Quality Windows Print Service - Complete Self-Contained Package
|
|
||||||
|
|
||||||
## 🎯 Overview
|
|
||||||
This is a complete, self-contained Windows print service for Quality Label system with zero external dependencies.
|
|
||||||
|
|
||||||
## 📦 Package Contents
|
|
||||||
|
|
||||||
### Core Files:
|
|
||||||
- `print_service_complete.py` - Main Python service (uses only standard library)
|
|
||||||
- `install_service_complete.bat` - Complete installer (run as Administrator)
|
|
||||||
- `uninstall_service_complete.bat` - Complete uninstaller
|
|
||||||
- `requirements_complete.txt` - Dependencies list (all standard library)
|
|
||||||
|
|
||||||
### Chrome Extension:
|
|
||||||
- `chrome_extension/` - Complete Chrome extension for web integration
|
|
||||||
- `chrome_extension/manifest.json` - Extension configuration
|
|
||||||
- `chrome_extension/background.js` - Service communication
|
|
||||||
- `chrome_extension/popup.html` - Extension interface
|
|
||||||
|
|
||||||
### Documentation:
|
|
||||||
- `README.md` - This file
|
|
||||||
- `PORTABLE_PYTHON_INSTRUCTIONS.txt` - Guide for Python distribution
|
|
||||||
- `INSTALLATION_COMPLETE.md` - Detailed installation guide
|
|
||||||
|
|
||||||
### Optional Build Tools:
|
|
||||||
- `build_executable.bat` - Creates standalone .exe (requires PyInstaller)
|
|
||||||
- `build_package.py` - Package builder script
|
|
||||||
|
|
||||||
## 🚀 Quick Installation (5 Minutes)
|
|
||||||
|
|
||||||
### Prerequisites:
|
|
||||||
- Windows 10/11 or Windows Server 2016+
|
|
||||||
- Administrator privileges
|
|
||||||
- Python 3.7+ (or use included portable Python)
|
|
||||||
- Google Chrome browser
|
|
||||||
|
|
||||||
### Steps:
|
|
||||||
1. **Extract Package**: Extract all files to a temporary location
|
|
||||||
2. **Run Installer**: Right-click `install_service_complete.bat` → "Run as administrator"
|
|
||||||
3. **Install Extension**: Load `chrome_extension` folder in Chrome (chrome://extensions/)
|
|
||||||
4. **Test Service**: Visit http://localhost:8765/health
|
|
||||||
|
|
||||||
## 🔧 Technical Details
|
|
||||||
|
|
||||||
### Service Architecture:
|
|
||||||
```
|
|
||||||
Web App → Chrome Extension → Windows Service → Printer
|
|
||||||
```
|
|
||||||
|
|
||||||
### Features:
|
|
||||||
- ✅ Pure Python implementation (standard library only)
|
|
||||||
- ✅ Multiple printing methods (Adobe Reader, SumatraPDF, PowerShell, Edge, System Default)
|
|
||||||
- ✅ Automatic service recovery and restart
|
|
||||||
- ✅ Comprehensive logging system
|
|
||||||
- ✅ Cross-printer compatibility
|
|
||||||
- ✅ Zero external dependencies
|
|
||||||
- ✅ Windows service integration
|
|
||||||
- ✅ Chrome extension communication
|
|
||||||
|
|
||||||
### Service Endpoints:
|
|
||||||
- `GET /health` - Service health check
|
|
||||||
- `GET /printers` - List available printers
|
|
||||||
- `GET /status` - Service status and statistics
|
|
||||||
- `POST /print_pdf` - Print PDF file
|
|
||||||
|
|
||||||
### Printing Methods (Fallback Chain):
|
|
||||||
1. Adobe Reader command line
|
|
||||||
2. SumatraPDF automation
|
|
||||||
3. PowerShell printing
|
|
||||||
4. Microsoft Edge integration
|
|
||||||
5. System default application
|
|
||||||
|
|
||||||
## 🛠️ Advanced Configuration
|
|
||||||
|
|
||||||
### Service Configuration:
|
|
||||||
- Service Name: `QualityPrintService`
|
|
||||||
- Display Name: `Quality Label Print Service`
|
|
||||||
- Installation Path: `C:\QualityPrintService\`
|
|
||||||
- Log Directory: `%USERPROFILE%\PrintService\logs\`
|
|
||||||
- Port: `8765` (localhost only)
|
|
||||||
|
|
||||||
### Logging:
|
|
||||||
- Daily log rotation
|
|
||||||
- Separate error and output logs
|
|
||||||
- Configurable log levels
|
|
||||||
- Automatic cleanup
|
|
||||||
|
|
||||||
### Recovery Options:
|
|
||||||
- Auto-restart on failure (3 attempts)
|
|
||||||
- 5-second delay between restarts
|
|
||||||
- 24-hour reset period
|
|
||||||
- Scheduled task fallback
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Service Won't Start:
|
|
||||||
1. Check Windows Event Viewer
|
|
||||||
2. Verify Python installation
|
|
||||||
3. Check port 8765 availability
|
|
||||||
4. Run as Administrator
|
|
||||||
|
|
||||||
### Printing Issues:
|
|
||||||
1. Verify printer installation
|
|
||||||
2. Check printer permissions
|
|
||||||
3. Test with different print methods
|
|
||||||
4. Review service logs
|
|
||||||
|
|
||||||
### Extension Issues:
|
|
||||||
1. Reload extension in Chrome
|
|
||||||
2. Check extension permissions
|
|
||||||
3. Verify service connectivity
|
|
||||||
4. Clear browser cache
|
|
||||||
|
|
||||||
### Common Solutions:
|
|
||||||
```bash
|
|
||||||
# Check service status
|
|
||||||
sc query QualityPrintService
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
sc start QualityPrintService
|
|
||||||
|
|
||||||
# Test connectivity
|
|
||||||
curl http://localhost:8765/health
|
|
||||||
|
|
||||||
# View logs
|
|
||||||
type "%USERPROFILE%\PrintService\logs\print_service_*.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 System Requirements
|
|
||||||
|
|
||||||
### Minimum Requirements:
|
|
||||||
- Windows 10 (1903) or Windows Server 2016
|
|
||||||
- 50 MB free disk space
|
|
||||||
- Python 3.7+ (can be portable)
|
|
||||||
- Chrome/Edge browser
|
|
||||||
- Local printer access
|
|
||||||
|
|
||||||
### Recommended:
|
|
||||||
- Windows 10/11 (latest)
|
|
||||||
- 100 MB free disk space
|
|
||||||
- Python 3.9+
|
|
||||||
- Administrative privileges
|
|
||||||
- Network printer access
|
|
||||||
|
|
||||||
## 🔒 Security Notes
|
|
||||||
- Service runs on localhost only (127.0.0.1:8765)
|
|
||||||
- No external network access required
|
|
||||||
- Uses Windows authentication
|
|
||||||
- Temporary files auto-cleanup
|
|
||||||
- Secure PDF handling
|
|
||||||
|
|
||||||
## 🚀 Performance
|
|
||||||
- Memory usage: ~15-30 MB
|
|
||||||
- CPU usage: <1% (idle)
|
|
||||||
- Startup time: ~2 seconds
|
|
||||||
- Print processing: ~1-3 seconds per job
|
|
||||||
|
|
||||||
## 📞 Support
|
|
||||||
For issues or questions:
|
|
||||||
1. Check this README
|
|
||||||
2. Review log files
|
|
||||||
3. Test with different browsers
|
|
||||||
4. Verify printer connectivity
|
|
||||||
|
|
||||||
## 📝 License
|
|
||||||
Internal use only - Quality Label System
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
# Windows Print Service - Error 1053 Troubleshooting Guide
|
|
||||||
|
|
||||||
## 🚨 Windows Service Error 1053 - "Service did not respond to start or control request"
|
|
||||||
|
|
||||||
This error occurs when Windows services don't communicate properly with the Service Control Manager (SCM). Here's how to fix it:
|
|
||||||
|
|
||||||
### 🔧 SOLUTION 1: Use the Enhanced Service Package
|
|
||||||
|
|
||||||
**Problem**: The original service wasn't designed for Windows service requirements.
|
|
||||||
**Fix**: Updated service architecture with proper Windows service communication.
|
|
||||||
|
|
||||||
#### New Files Included:
|
|
||||||
- ✅ `service_wrapper.py` - Handles Windows service communication
|
|
||||||
- ✅ `print_service_complete.py` - Enhanced with signal handling and proper shutdown
|
|
||||||
- ✅ `test_service.bat` - Test service in standalone mode before installing
|
|
||||||
|
|
||||||
### 🧪 STEP-BY-STEP TROUBLESHOOTING:
|
|
||||||
|
|
||||||
#### Step 1: Test Service in Standalone Mode
|
|
||||||
```cmd
|
|
||||||
# Run this to test the service before installing as Windows service
|
|
||||||
test_service.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
If this works, the service code is fine. If not, check the logs.
|
|
||||||
|
|
||||||
#### Step 2: Check Python and Dependencies
|
|
||||||
```cmd
|
|
||||||
# Verify Python embedded is working
|
|
||||||
cd C:\QualityPrintService\python_embedded
|
|
||||||
python.exe --version
|
|
||||||
|
|
||||||
# Test the service script directly
|
|
||||||
python.exe ..\print_service_complete.py --test
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 3: Install with Enhanced Wrapper
|
|
||||||
The installer now creates a service wrapper that properly communicates with Windows SCM:
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
# Uninstall old service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
sc delete QualityPrintService
|
|
||||||
|
|
||||||
# Reinstall with enhanced wrapper
|
|
||||||
install_service_complete.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 4: Manual Service Control
|
|
||||||
```cmd
|
|
||||||
# Start service manually to see error details
|
|
||||||
net start QualityPrintService
|
|
||||||
|
|
||||||
# Check service status
|
|
||||||
sc query QualityPrintService
|
|
||||||
|
|
||||||
# View service logs
|
|
||||||
type "%USERPROFILE%\PrintService\logs\service_wrapper_*.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 🔍 DIAGNOSTIC COMMANDS:
|
|
||||||
|
|
||||||
#### Check Service Installation:
|
|
||||||
```cmd
|
|
||||||
sc query QualityPrintService
|
|
||||||
sc qc QualityPrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Check Port Availability:
|
|
||||||
```cmd
|
|
||||||
netstat -an | findstr :8765
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Test HTTP Endpoints:
|
|
||||||
```cmd
|
|
||||||
curl http://localhost:8765/health
|
|
||||||
# OR
|
|
||||||
powershell Invoke-WebRequest -Uri "http://localhost:8765/health"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 📋 COMMON SOLUTIONS:
|
|
||||||
|
|
||||||
#### Solution A: Port Already in Use
|
|
||||||
```cmd
|
|
||||||
# Find process using port 8765
|
|
||||||
netstat -ano | findstr :8765
|
|
||||||
# Kill process if needed (replace PID)
|
|
||||||
taskkill /PID <PID_NUMBER> /F
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Solution B: Python Path Issues
|
|
||||||
```cmd
|
|
||||||
# Verify Python embedded path in service wrapper
|
|
||||||
type "C:\QualityPrintService\service_wrapper.bat"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Solution C: Permissions Issues
|
|
||||||
```cmd
|
|
||||||
# Run installer as Administrator
|
|
||||||
# Right-click install_service_complete.bat → "Run as administrator"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Solution D: Service Recovery
|
|
||||||
```cmd
|
|
||||||
# Configure automatic recovery
|
|
||||||
sc failure QualityPrintService reset= 86400 actions= restart/5000/restart/5000/restart/5000
|
|
||||||
```
|
|
||||||
|
|
||||||
### 📊 SERVICE STATUS VERIFICATION:
|
|
||||||
|
|
||||||
#### Successful Service Start:
|
|
||||||
- Service Status: RUNNING
|
|
||||||
- HTTP Response: `{"status": "healthy", "service": "Windows Print Service"}`
|
|
||||||
- Log Shows: "Service is ready and listening..."
|
|
||||||
|
|
||||||
#### Failed Service Start:
|
|
||||||
- Service Status: STOPPED or START_PENDING
|
|
||||||
- HTTP Response: Connection refused
|
|
||||||
- Log Shows: Error messages with specific details
|
|
||||||
|
|
||||||
### 🛠️ ADVANCED TROUBLESHOOTING:
|
|
||||||
|
|
||||||
#### Enable Debug Logging:
|
|
||||||
Edit the service script to increase logging level:
|
|
||||||
```python
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Manual Service Wrapper Test:
|
|
||||||
```cmd
|
|
||||||
cd C:\QualityPrintService
|
|
||||||
python_embedded\python.exe service_wrapper.py
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Windows Event Viewer:
|
|
||||||
1. Open Event Viewer
|
|
||||||
2. Navigate: Windows Logs → Application
|
|
||||||
3. Filter by Source: Service Control Manager
|
|
||||||
4. Look for QualityPrintService errors
|
|
||||||
|
|
||||||
### 📞 SUPPORT CHECKLIST:
|
|
||||||
|
|
||||||
Before reporting issues, please verify:
|
|
||||||
|
|
||||||
- [ ] ✅ Python embedded is working (`python_embedded\python.exe --version`)
|
|
||||||
- [ ] ✅ Service runs in standalone mode (`test_service.bat`)
|
|
||||||
- [ ] ✅ Port 8765 is available (`netstat -an | findstr :8765`)
|
|
||||||
- [ ] ✅ Installer was run as Administrator
|
|
||||||
- [ ] ✅ Windows is Windows 10/11 or Server 2016+
|
|
||||||
- [ ] ✅ Service logs show specific error messages
|
|
||||||
|
|
||||||
### 🎯 EXPECTED RESULTS:
|
|
||||||
|
|
||||||
After following this guide:
|
|
||||||
|
|
||||||
1. **Service Status**: RUNNING
|
|
||||||
2. **Health Check**: http://localhost:8765/health returns JSON response
|
|
||||||
3. **Chrome Extension**: Detects service and shows "Windows Service" mode
|
|
||||||
4. **Printing**: Silent PDF printing works without dialogs
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Package Version**: Zero Dependencies Complete
|
|
||||||
**Last Updated**: September 2025
|
|
||||||
**Support**: Check service logs in `%USERPROFILE%\PrintService\logs\`
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo Building standalone Windows Print Service executable...
|
|
||||||
|
|
||||||
REM Check if PyInstaller is available
|
|
||||||
pip show pyinstaller >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo Installing PyInstaller...
|
|
||||||
pip install pyinstaller
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Build standalone executable
|
|
||||||
echo Creating standalone executable...
|
|
||||||
pyinstaller --onefile --noconsole --name="QualityPrintService" print_service_complete.py
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo.
|
|
||||||
echo ✓ Executable created successfully!
|
|
||||||
echo Location: dist\QualityPrintService.exe
|
|
||||||
echo.
|
|
||||||
echo You can now distribute the .exe file instead of the Python script
|
|
||||||
echo Update install_service_complete.bat to use the .exe file
|
|
||||||
) else (
|
|
||||||
echo ERROR: Failed to create executable
|
|
||||||
)
|
|
||||||
|
|
||||||
pause
|
|
||||||
@@ -1,586 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Build script to create a complete self-contained Windows Print Service package
|
|
||||||
This script prepares all dependencies and creates the distribution package
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import zipfile
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def create_portable_python_package():
|
|
||||||
"""Create instructions for portable Python package."""
|
|
||||||
instructions = """
|
|
||||||
# PORTABLE PYTHON PACKAGE INSTRUCTIONS
|
|
||||||
|
|
||||||
To create a complete self-contained package, you need to include a portable Python interpreter.
|
|
||||||
|
|
||||||
## Option 1: Download Embedded Python (Recommended)
|
|
||||||
1. Download Python 3.11 Embedded from: https://www.python.org/downloads/windows/
|
|
||||||
2. Choose "Windows embeddable package (64-bit)" or "(32-bit)"
|
|
||||||
3. Extract to a folder named 'python_portable'
|
|
||||||
4. The structure should be:
|
|
||||||
python_portable/
|
|
||||||
├── python.exe
|
|
||||||
├── python311.dll (or similar)
|
|
||||||
├── pythoncom311.dll
|
|
||||||
└── ... (other Python files)
|
|
||||||
|
|
||||||
## Option 2: Use PyInstaller (Alternative)
|
|
||||||
1. Install PyInstaller: pip install pyinstaller
|
|
||||||
2. Run: pyinstaller --onefile --noconsole print_service_complete.py
|
|
||||||
3. This creates a single .exe file with all dependencies
|
|
||||||
|
|
||||||
## Option 3: Manual Python Installation Check
|
|
||||||
The installer will check for system Python and use it if available.
|
|
||||||
|
|
||||||
## Current Package Structure
|
|
||||||
This package includes:
|
|
||||||
✓ Complete Python script with all dependencies
|
|
||||||
✓ Windows service installer
|
|
||||||
✓ Chrome extension
|
|
||||||
✓ Logging and error handling
|
|
||||||
✓ Multiple printing method fallbacks
|
|
||||||
✓ Automatic recovery options
|
|
||||||
|
|
||||||
## Dependencies Included in Python Script:
|
|
||||||
- All standard library modules (http.server, json, subprocess, etc.)
|
|
||||||
- No external dependencies required
|
|
||||||
- Pure Python implementation
|
|
||||||
|
|
||||||
The service will work with any Python 3.7+ installation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open('PORTABLE_PYTHON_INSTRUCTIONS.txt', 'w') as f:
|
|
||||||
f.write(instructions)
|
|
||||||
|
|
||||||
print("✓ Created portable Python instructions")
|
|
||||||
|
|
||||||
def create_build_executable_script():
|
|
||||||
"""Create script to build standalone executable."""
|
|
||||||
build_script = """@echo off
|
|
||||||
echo Building standalone Windows Print Service executable...
|
|
||||||
|
|
||||||
REM Check if PyInstaller is available
|
|
||||||
pip show pyinstaller >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo Installing PyInstaller...
|
|
||||||
pip install pyinstaller
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Build standalone executable
|
|
||||||
echo Creating standalone executable...
|
|
||||||
pyinstaller --onefile --noconsole --name="QualityPrintService" print_service_complete.py
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo.
|
|
||||||
echo ✓ Executable created successfully!
|
|
||||||
echo Location: dist\\QualityPrintService.exe
|
|
||||||
echo.
|
|
||||||
echo You can now distribute the .exe file instead of the Python script
|
|
||||||
echo Update install_service_complete.bat to use the .exe file
|
|
||||||
) else (
|
|
||||||
echo ERROR: Failed to create executable
|
|
||||||
)
|
|
||||||
|
|
||||||
pause
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open('build_executable.bat', 'w') as f:
|
|
||||||
f.write(build_script)
|
|
||||||
|
|
||||||
print("✓ Created executable build script")
|
|
||||||
|
|
||||||
def create_complete_readme():
|
|
||||||
"""Create comprehensive README for the package."""
|
|
||||||
readme_content = """# Quality Windows Print Service - Complete Self-Contained Package
|
|
||||||
|
|
||||||
## 🎯 Overview
|
|
||||||
This is a complete, self-contained Windows print service for Quality Label system with zero external dependencies.
|
|
||||||
|
|
||||||
## 📦 Package Contents
|
|
||||||
|
|
||||||
### Core Files:
|
|
||||||
- `print_service_complete.py` - Main Python service (uses only standard library)
|
|
||||||
- `install_service_complete.bat` - Complete installer (run as Administrator)
|
|
||||||
- `uninstall_service_complete.bat` - Complete uninstaller
|
|
||||||
- `requirements_complete.txt` - Dependencies list (all standard library)
|
|
||||||
|
|
||||||
### Chrome Extension:
|
|
||||||
- `chrome_extension/` - Complete Chrome extension for web integration
|
|
||||||
- `chrome_extension/manifest.json` - Extension configuration
|
|
||||||
- `chrome_extension/background.js` - Service communication
|
|
||||||
- `chrome_extension/popup.html` - Extension interface
|
|
||||||
|
|
||||||
### Documentation:
|
|
||||||
- `README.md` - This file
|
|
||||||
- `PORTABLE_PYTHON_INSTRUCTIONS.txt` - Guide for Python distribution
|
|
||||||
- `INSTALLATION_COMPLETE.md` - Detailed installation guide
|
|
||||||
|
|
||||||
### Optional Build Tools:
|
|
||||||
- `build_executable.bat` - Creates standalone .exe (requires PyInstaller)
|
|
||||||
- `build_package.py` - Package builder script
|
|
||||||
|
|
||||||
## 🚀 Quick Installation (5 Minutes)
|
|
||||||
|
|
||||||
### Prerequisites:
|
|
||||||
- Windows 10/11 or Windows Server 2016+
|
|
||||||
- Administrator privileges
|
|
||||||
- Python 3.7+ (or use included portable Python)
|
|
||||||
- Google Chrome browser
|
|
||||||
|
|
||||||
### Steps:
|
|
||||||
1. **Extract Package**: Extract all files to a temporary location
|
|
||||||
2. **Run Installer**: Right-click `install_service_complete.bat` → "Run as administrator"
|
|
||||||
3. **Install Extension**: Load `chrome_extension` folder in Chrome (chrome://extensions/)
|
|
||||||
4. **Test Service**: Visit http://localhost:8765/health
|
|
||||||
|
|
||||||
## 🔧 Technical Details
|
|
||||||
|
|
||||||
### Service Architecture:
|
|
||||||
```
|
|
||||||
Web App → Chrome Extension → Windows Service → Printer
|
|
||||||
```
|
|
||||||
|
|
||||||
### Features:
|
|
||||||
- ✅ Pure Python implementation (standard library only)
|
|
||||||
- ✅ Multiple printing methods (Adobe Reader, SumatraPDF, PowerShell, Edge, System Default)
|
|
||||||
- ✅ Automatic service recovery and restart
|
|
||||||
- ✅ Comprehensive logging system
|
|
||||||
- ✅ Cross-printer compatibility
|
|
||||||
- ✅ Zero external dependencies
|
|
||||||
- ✅ Windows service integration
|
|
||||||
- ✅ Chrome extension communication
|
|
||||||
|
|
||||||
### Service Endpoints:
|
|
||||||
- `GET /health` - Service health check
|
|
||||||
- `GET /printers` - List available printers
|
|
||||||
- `GET /status` - Service status and statistics
|
|
||||||
- `POST /print_pdf` - Print PDF file
|
|
||||||
|
|
||||||
### Printing Methods (Fallback Chain):
|
|
||||||
1. Adobe Reader command line
|
|
||||||
2. SumatraPDF automation
|
|
||||||
3. PowerShell printing
|
|
||||||
4. Microsoft Edge integration
|
|
||||||
5. System default application
|
|
||||||
|
|
||||||
## 🛠️ Advanced Configuration
|
|
||||||
|
|
||||||
### Service Configuration:
|
|
||||||
- Service Name: `QualityPrintService`
|
|
||||||
- Display Name: `Quality Label Print Service`
|
|
||||||
- Installation Path: `C:\\QualityPrintService\\`
|
|
||||||
- Log Directory: `%USERPROFILE%\\PrintService\\logs\\`
|
|
||||||
- Port: `8765` (localhost only)
|
|
||||||
|
|
||||||
### Logging:
|
|
||||||
- Daily log rotation
|
|
||||||
- Separate error and output logs
|
|
||||||
- Configurable log levels
|
|
||||||
- Automatic cleanup
|
|
||||||
|
|
||||||
### Recovery Options:
|
|
||||||
- Auto-restart on failure (3 attempts)
|
|
||||||
- 5-second delay between restarts
|
|
||||||
- 24-hour reset period
|
|
||||||
- Scheduled task fallback
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Service Won't Start:
|
|
||||||
1. Check Windows Event Viewer
|
|
||||||
2. Verify Python installation
|
|
||||||
3. Check port 8765 availability
|
|
||||||
4. Run as Administrator
|
|
||||||
|
|
||||||
### Printing Issues:
|
|
||||||
1. Verify printer installation
|
|
||||||
2. Check printer permissions
|
|
||||||
3. Test with different print methods
|
|
||||||
4. Review service logs
|
|
||||||
|
|
||||||
### Extension Issues:
|
|
||||||
1. Reload extension in Chrome
|
|
||||||
2. Check extension permissions
|
|
||||||
3. Verify service connectivity
|
|
||||||
4. Clear browser cache
|
|
||||||
|
|
||||||
### Common Solutions:
|
|
||||||
```bash
|
|
||||||
# Check service status
|
|
||||||
sc query QualityPrintService
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
sc start QualityPrintService
|
|
||||||
|
|
||||||
# Test connectivity
|
|
||||||
curl http://localhost:8765/health
|
|
||||||
|
|
||||||
# View logs
|
|
||||||
type "%USERPROFILE%\\PrintService\\logs\\print_service_*.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 System Requirements
|
|
||||||
|
|
||||||
### Minimum Requirements:
|
|
||||||
- Windows 10 (1903) or Windows Server 2016
|
|
||||||
- 50 MB free disk space
|
|
||||||
- Python 3.7+ (can be portable)
|
|
||||||
- Chrome/Edge browser
|
|
||||||
- Local printer access
|
|
||||||
|
|
||||||
### Recommended:
|
|
||||||
- Windows 10/11 (latest)
|
|
||||||
- 100 MB free disk space
|
|
||||||
- Python 3.9+
|
|
||||||
- Administrative privileges
|
|
||||||
- Network printer access
|
|
||||||
|
|
||||||
## 🔒 Security Notes
|
|
||||||
- Service runs on localhost only (127.0.0.1:8765)
|
|
||||||
- No external network access required
|
|
||||||
- Uses Windows authentication
|
|
||||||
- Temporary files auto-cleanup
|
|
||||||
- Secure PDF handling
|
|
||||||
|
|
||||||
## 🚀 Performance
|
|
||||||
- Memory usage: ~15-30 MB
|
|
||||||
- CPU usage: <1% (idle)
|
|
||||||
- Startup time: ~2 seconds
|
|
||||||
- Print processing: ~1-3 seconds per job
|
|
||||||
|
|
||||||
## 📞 Support
|
|
||||||
For issues or questions:
|
|
||||||
1. Check this README
|
|
||||||
2. Review log files
|
|
||||||
3. Test with different browsers
|
|
||||||
4. Verify printer connectivity
|
|
||||||
|
|
||||||
## 📝 License
|
|
||||||
Internal use only - Quality Label System
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open('README_COMPLETE.md', 'w') as f:
|
|
||||||
f.write(readme_content)
|
|
||||||
|
|
||||||
print("✓ Created comprehensive README")
|
|
||||||
|
|
||||||
def create_installation_guide():
|
|
||||||
"""Create detailed installation guide."""
|
|
||||||
guide_content = """# Quality Print Service - Complete Installation Guide
|
|
||||||
|
|
||||||
## 🎯 Pre-Installation Checklist
|
|
||||||
|
|
||||||
### System Requirements Verification:
|
|
||||||
- [ ] Windows 10 build 1903+ or Windows Server 2016+
|
|
||||||
- [ ] Administrator access to the system
|
|
||||||
- [ ] Google Chrome browser installed
|
|
||||||
- [ ] At least 100 MB free disk space
|
|
||||||
- [ ] Network/USB printer connected and configured
|
|
||||||
|
|
||||||
### Python Requirements:
|
|
||||||
- [ ] Python 3.7+ installed OR use included portable Python
|
|
||||||
- [ ] Python accessible via command line (optional)
|
|
||||||
|
|
||||||
## 📦 Installation Methods
|
|
||||||
|
|
||||||
### Method 1: Complete Automatic Installation (Recommended)
|
|
||||||
|
|
||||||
1. **Download and Extract**:
|
|
||||||
- Extract the complete package to any folder (e.g., Desktop)
|
|
||||||
- No need to keep the files permanently
|
|
||||||
|
|
||||||
2. **Run Installer**:
|
|
||||||
```
|
|
||||||
Right-click: install_service_complete.bat
|
|
||||||
Select: "Run as administrator"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Follow Installation Steps**:
|
|
||||||
```
|
|
||||||
[1/6] Administrator privileges confirmed ✓
|
|
||||||
[2/6] Checking Python installation...
|
|
||||||
[3/6] Creating installation directories...
|
|
||||||
[4/6] Installing service files...
|
|
||||||
[5/6] Installing Windows service...
|
|
||||||
[6/6] Starting service...
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Install Chrome Extension**:
|
|
||||||
- Chrome will open the extension folder automatically
|
|
||||||
- Go to `chrome://extensions/`
|
|
||||||
- Enable "Developer mode" (top right)
|
|
||||||
- Click "Load unpacked"
|
|
||||||
- Select the `chrome_extension` folder
|
|
||||||
|
|
||||||
5. **Verify Installation**:
|
|
||||||
- Visit: `http://localhost:8765/health`
|
|
||||||
- Expected response: `{"status": "healthy"}`
|
|
||||||
|
|
||||||
### Method 2: Manual Installation
|
|
||||||
|
|
||||||
1. **Create Directories**:
|
|
||||||
```cmd
|
|
||||||
mkdir C:\QualityPrintService
|
|
||||||
mkdir %USERPROFILE%\PrintService\logs
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Copy Files**:
|
|
||||||
```cmd
|
|
||||||
copy print_service_complete.py C:\QualityPrintService\
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Install Service**:
|
|
||||||
```cmd
|
|
||||||
sc create QualityPrintService binPath="python C:\QualityPrintService\print_service_complete.py"
|
|
||||||
sc start QualityPrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Post-Installation Configuration
|
|
||||||
|
|
||||||
### Service Verification:
|
|
||||||
```cmd
|
|
||||||
# Check service status
|
|
||||||
sc query QualityPrintService
|
|
||||||
|
|
||||||
# Check service configuration
|
|
||||||
sc qc QualityPrintService
|
|
||||||
|
|
||||||
# View service logs (if using NSSM)
|
|
||||||
type "%USERPROFILE%\PrintService\logs\service_output.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Network Testing:
|
|
||||||
```powershell
|
|
||||||
# Test health endpoint
|
|
||||||
Invoke-RestMethod -Uri http://localhost:8765/health
|
|
||||||
|
|
||||||
# Test printer endpoint
|
|
||||||
Invoke-RestMethod -Uri http://localhost:8765/printers
|
|
||||||
|
|
||||||
# Test from browser
|
|
||||||
start http://localhost:8765/status
|
|
||||||
```
|
|
||||||
|
|
||||||
### Chrome Extension Setup:
|
|
||||||
1. Open Chrome browser
|
|
||||||
2. Navigate to `chrome://extensions/`
|
|
||||||
3. Enable "Developer mode" toggle (top-right corner)
|
|
||||||
4. Click "Load unpacked" button
|
|
||||||
5. Browse and select the `chrome_extension` folder
|
|
||||||
6. Verify extension appears in the list with green toggle
|
|
||||||
|
|
||||||
## 🔍 Installation Verification
|
|
||||||
|
|
||||||
### Health Check Procedure:
|
|
||||||
1. **Service Status**: Verify Windows service is running
|
|
||||||
```cmd
|
|
||||||
sc query QualityPrintService | find "RUNNING"
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Network Connectivity**: Test HTTP endpoints
|
|
||||||
```cmd
|
|
||||||
curl http://localhost:8765/health
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Printer Detection**: Check printer enumeration
|
|
||||||
```cmd
|
|
||||||
curl http://localhost:8765/printers
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Extension Communication**: Test from web page
|
|
||||||
- Open the Quality app in Chrome
|
|
||||||
- Go to print module
|
|
||||||
- Verify "Extension ready" status
|
|
||||||
|
|
||||||
### Expected Responses:
|
|
||||||
|
|
||||||
**Health Check**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "healthy",
|
|
||||||
"service": "Windows Print Service",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"timestamp": "2025-09-25T10:30:00"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Printer List**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"printers": [
|
|
||||||
{"name": "HP LaserJet", "type": "Local", "status": "Available"}
|
|
||||||
],
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚨 Troubleshooting Common Issues
|
|
||||||
|
|
||||||
### Issue: "Administrator privileges required"
|
|
||||||
**Solution**:
|
|
||||||
- Right-click installer file
|
|
||||||
- Select "Run as administrator"
|
|
||||||
- Confirm UAC prompt
|
|
||||||
|
|
||||||
### Issue: "Python not found"
|
|
||||||
**Solutions**:
|
|
||||||
1. Install Python from python.org
|
|
||||||
2. Use included portable Python
|
|
||||||
3. Add Python to system PATH
|
|
||||||
|
|
||||||
### Issue: "Service failed to start"
|
|
||||||
**Solutions**:
|
|
||||||
1. Check Windows Event Viewer:
|
|
||||||
- Windows Logs → Application
|
|
||||||
- Filter by source: "Service Control Manager"
|
|
||||||
2. Verify port 8765 is not in use:
|
|
||||||
```cmd
|
|
||||||
netstat -an | find "8765"
|
|
||||||
```
|
|
||||||
3. Check service logs:
|
|
||||||
```cmd
|
|
||||||
type "%USERPROFILE%\PrintService\logs\print_service_*.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue: "Chrome extension not working"
|
|
||||||
**Solutions**:
|
|
||||||
1. Reload extension in `chrome://extensions/`
|
|
||||||
2. Check extension permissions
|
|
||||||
3. Verify service is responding at `localhost:8765`
|
|
||||||
4. Clear browser cache and cookies
|
|
||||||
|
|
||||||
### Issue: "PDF printing fails"
|
|
||||||
**Solutions**:
|
|
||||||
1. Install Adobe Reader or SumatraPDF
|
|
||||||
2. Check printer permissions
|
|
||||||
3. Verify PDF file accessibility
|
|
||||||
4. Test with different printer
|
|
||||||
|
|
||||||
## 🔄 Maintenance and Updates
|
|
||||||
|
|
||||||
### Regular Maintenance:
|
|
||||||
- **Log Cleanup**: Logs rotate automatically
|
|
||||||
- **Service Monitoring**: Check service status weekly
|
|
||||||
- **Chrome Extension**: Update when prompted
|
|
||||||
|
|
||||||
### Manual Service Management:
|
|
||||||
```cmd
|
|
||||||
# Stop service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
|
|
||||||
# Start service
|
|
||||||
sc start QualityPrintService
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
sc stop QualityPrintService && timeout /t 3 && sc start QualityPrintService
|
|
||||||
|
|
||||||
# Change startup type
|
|
||||||
sc config QualityPrintService start= auto
|
|
||||||
```
|
|
||||||
|
|
||||||
### Log File Locations:
|
|
||||||
- Service logs: `%USERPROFILE%\PrintService\logs\`
|
|
||||||
- Windows Event Logs: Event Viewer → Windows Logs → Application
|
|
||||||
- Chrome Extension: Chrome DevTools → Console
|
|
||||||
|
|
||||||
## 🔧 Advanced Configuration
|
|
||||||
|
|
||||||
### Custom Port Configuration:
|
|
||||||
Edit `print_service_complete.py`:
|
|
||||||
```python
|
|
||||||
server_address = ('localhost', 8765) # Change 8765 to desired port
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Install Directory:
|
|
||||||
Edit `install_service_complete.bat`:
|
|
||||||
```batch
|
|
||||||
set INSTALL_DIR=C:\CustomPath\PrintService
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Recovery Options:
|
|
||||||
```cmd
|
|
||||||
sc failure QualityPrintService reset= 86400 actions= restart/5000/restart/10000/restart/30000
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 Uninstallation
|
|
||||||
|
|
||||||
### Complete Removal:
|
|
||||||
1. Run `uninstall_service_complete.bat` as Administrator
|
|
||||||
2. Remove Chrome extension manually
|
|
||||||
3. Optional: Delete log files
|
|
||||||
|
|
||||||
### Manual Removal:
|
|
||||||
```cmd
|
|
||||||
# Stop and remove service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
sc delete QualityPrintService
|
|
||||||
|
|
||||||
# Remove files
|
|
||||||
rmdir /s /q C:\QualityPrintService
|
|
||||||
rmdir /s /q "%USERPROFILE%\PrintService"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📞 Getting Help
|
|
||||||
|
|
||||||
### Before Contacting Support:
|
|
||||||
1. Check this installation guide
|
|
||||||
2. Review troubleshooting section
|
|
||||||
3. Check service logs for error messages
|
|
||||||
4. Test with simple printer (like Microsoft Print to PDF)
|
|
||||||
|
|
||||||
### Information to Provide:
|
|
||||||
- Windows version (run `winver`)
|
|
||||||
- Python version (run `python --version`)
|
|
||||||
- Service status (run `sc query QualityPrintService`)
|
|
||||||
- Recent log entries
|
|
||||||
- Error messages or screenshots
|
|
||||||
|
|
||||||
---
|
|
||||||
**Installation Guide Version**: 1.0.0
|
|
||||||
**Last Updated**: September 2025
|
|
||||||
**Support**: Internal Quality System Team
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open('INSTALLATION_COMPLETE.md', 'w') as f:
|
|
||||||
f.write(guide_content)
|
|
||||||
|
|
||||||
print("✓ Created detailed installation guide")
|
|
||||||
|
|
||||||
def build_complete_package():
|
|
||||||
"""Build the complete package with all dependencies."""
|
|
||||||
print("Building complete Windows Print Service package...")
|
|
||||||
|
|
||||||
# Create documentation files
|
|
||||||
create_portable_python_package()
|
|
||||||
create_build_executable_script()
|
|
||||||
create_complete_readme()
|
|
||||||
create_installation_guide()
|
|
||||||
|
|
||||||
print("\n✓ Complete package prepared!")
|
|
||||||
print("\nFiles created:")
|
|
||||||
print(" ✓ print_service_complete.py - Main service with all dependencies")
|
|
||||||
print(" ✓ install_service_complete.bat - Complete installer")
|
|
||||||
print(" ✓ uninstall_service_complete.bat - Complete uninstaller")
|
|
||||||
print(" ✓ requirements_complete.txt - Dependencies list")
|
|
||||||
print(" ✓ README_COMPLETE.md - Comprehensive documentation")
|
|
||||||
print(" ✓ INSTALLATION_COMPLETE.md - Detailed installation guide")
|
|
||||||
print(" ✓ PORTABLE_PYTHON_INSTRUCTIONS.txt - Python distribution guide")
|
|
||||||
print(" ✓ build_executable.bat - Executable builder")
|
|
||||||
|
|
||||||
print("\nNext steps:")
|
|
||||||
print("1. Include a portable Python distribution (see PORTABLE_PYTHON_INSTRUCTIONS.txt)")
|
|
||||||
print("2. Test the complete package on a clean Windows system")
|
|
||||||
print("3. Package everything into the download ZIP")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
build_complete_package()
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
/**
|
|
||||||
* Quality Label Printing Extension - Windows Service Communication
|
|
||||||
* Communicates with local Windows print service for silent printing
|
|
||||||
*/
|
|
||||||
|
|
||||||
console.log('Quality Label Printing Extension - Windows Service Mode');
|
|
||||||
|
|
||||||
// Service configuration
|
|
||||||
const PRINT_SERVICE_URL = 'http://localhost:8765';
|
|
||||||
const SERVICE_TIMEOUT = 30000; // 30 seconds
|
|
||||||
|
|
||||||
// Initialize extension
|
|
||||||
chrome.runtime.onInstalled.addListener(() => {
|
|
||||||
console.log('Quality Label Printing Service extension installed - Windows Service Mode');
|
|
||||||
testServiceConnection();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test connection to Windows service
|
|
||||||
async function testServiceConnection() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${PRINT_SERVICE_URL}/health`, {
|
|
||||||
method: 'GET',
|
|
||||||
timeout: 5000
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
console.log('✅ Windows Print Service connected:', data);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
console.warn('⚠️ Windows Print Service not responding:', response.status);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('❌ Windows Print Service not available:', error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle messages from content scripts or web pages
|
|
||||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
||||||
console.log('Background script received message:', message);
|
|
||||||
|
|
||||||
switch (message.action) {
|
|
||||||
case 'print_pdf':
|
|
||||||
handleWindowsServicePrint(message)
|
|
||||||
.then(result => {
|
|
||||||
console.log('Windows service print completed:', result);
|
|
||||||
sendResponse(result);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Windows service print error:', error);
|
|
||||||
sendResponse({ success: false, error: error.message });
|
|
||||||
});
|
|
||||||
return true; // Keep message channel open for async response
|
|
||||||
|
|
||||||
case 'get_printers':
|
|
||||||
getAvailablePrinters()
|
|
||||||
.then(printers => {
|
|
||||||
sendResponse({ success: true, printers: printers });
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error getting printers:', error);
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
error: error.message,
|
|
||||||
printers: [{ name: 'default', display_name: 'Default Printer', is_default: true }]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'ping':
|
|
||||||
testServiceConnection()
|
|
||||||
.then(connected => {
|
|
||||||
sendResponse({
|
|
||||||
success: true,
|
|
||||||
extension_version: chrome.runtime.getManifest().version,
|
|
||||||
ready: true,
|
|
||||||
service_connected: connected,
|
|
||||||
mode: 'windows_service'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
sendResponse({
|
|
||||||
success: true,
|
|
||||||
extension_version: chrome.runtime.getManifest().version,
|
|
||||||
ready: false,
|
|
||||||
service_connected: false,
|
|
||||||
mode: 'windows_service'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
sendResponse({ error: 'Unknown action', success: false });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle external messages from web pages
|
|
||||||
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
|
|
||||||
console.log('External message received:', message, 'from:', sender);
|
|
||||||
|
|
||||||
// Verify sender origin for security
|
|
||||||
const allowedOrigins = [
|
|
||||||
'http://localhost:5000',
|
|
||||||
'http://localhost:8000',
|
|
||||||
'http://127.0.0.1:5000',
|
|
||||||
'http://127.0.0.1:8000',
|
|
||||||
'http://localhost:3000',
|
|
||||||
'http://localhost:8080'
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!allowedOrigins.some(origin => sender.url && sender.url.startsWith(origin))) {
|
|
||||||
console.warn('Unauthorized origin:', sender.url);
|
|
||||||
sendResponse({ error: 'Unauthorized origin', success: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the message
|
|
||||||
switch (message.action) {
|
|
||||||
case 'print_pdf':
|
|
||||||
handleWindowsServicePrint(message)
|
|
||||||
.then(result => sendResponse(result))
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Print PDF error:', error);
|
|
||||||
sendResponse({ success: false, error: error.message });
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'ping':
|
|
||||||
testServiceConnection()
|
|
||||||
.then(connected => {
|
|
||||||
sendResponse({
|
|
||||||
success: true,
|
|
||||||
extension_version: chrome.runtime.getManifest().version,
|
|
||||||
ready: true,
|
|
||||||
service_connected: connected,
|
|
||||||
mode: 'windows_service'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
sendResponse({
|
|
||||||
success: true,
|
|
||||||
extension_version: chrome.runtime.getManifest().version,
|
|
||||||
ready: false,
|
|
||||||
service_connected: false,
|
|
||||||
mode: 'windows_service'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
sendResponse({ error: 'Unknown action', success: false });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available printers from Windows service
|
|
||||||
*/
|
|
||||||
async function getAvailablePrinters() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${PRINT_SERVICE_URL}/printers`, {
|
|
||||||
method: 'GET',
|
|
||||||
timeout: 10000
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
return data.printers || [];
|
|
||||||
} else {
|
|
||||||
throw new Error(`Service responded with status: ${response.status}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to get printers from service:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle PDF printing via Windows Service
|
|
||||||
*/
|
|
||||||
async function handleWindowsServicePrint(message) {
|
|
||||||
console.log('🖨️ Sending PDF to Windows print service:', message);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { pdfUrl, orderId, prodOrder, quantity, printerName } = message;
|
|
||||||
|
|
||||||
if (!pdfUrl) {
|
|
||||||
throw new Error('PDF URL is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, test if service is available
|
|
||||||
const serviceAvailable = await testServiceConnection();
|
|
||||||
if (!serviceAvailable) {
|
|
||||||
throw new Error('Windows Print Service is not running. Please ensure the service is installed and started.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare print request
|
|
||||||
const printRequest = {
|
|
||||||
pdf_url: pdfUrl,
|
|
||||||
printer_name: printerName || 'default',
|
|
||||||
order_id: orderId,
|
|
||||||
prod_order: prodOrder,
|
|
||||||
quantity: quantity,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('📤 Sending print request to service:', printRequest);
|
|
||||||
|
|
||||||
// Send PDF to Windows service for printing
|
|
||||||
const response = await fetch(`${PRINT_SERVICE_URL}/print_pdf`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(printRequest)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
throw new Error(`Print service error (${response.status}): ${errorText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
console.log('✅ Print job sent successfully:', result);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: `PDF sent to ${result.printer || printerName || 'default printer'} successfully`,
|
|
||||||
method: result.method || 'Windows Print Service',
|
|
||||||
printer: result.printer || printerName,
|
|
||||||
orderId: orderId,
|
|
||||||
instruction: 'PDF has been sent to the printer queue'
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
throw new Error(result.error || 'Unknown print service error');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Windows service print failed:', error);
|
|
||||||
|
|
||||||
// Fallback: Return instruction to print manually
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error.message,
|
|
||||||
fallback: true,
|
|
||||||
instruction: 'Windows service unavailable. Please download and print the PDF manually.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fallback function for when Windows service is not available
|
|
||||||
*/
|
|
||||||
async function handleFallbackPrint(message) {
|
|
||||||
console.log('🔄 Using fallback print method:', message);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { pdfUrl, orderId, prodOrder, quantity } = message;
|
|
||||||
|
|
||||||
// Create a new tab with the PDF
|
|
||||||
const tab = await chrome.tabs.create({
|
|
||||||
url: pdfUrl,
|
|
||||||
active: false // Don't switch to the tab
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait a moment for PDF to load
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
|
|
||||||
// Get the created tab
|
|
||||||
const updatedTab = await chrome.tabs.get(tab.id);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: `PDF opened in new tab for manual printing`,
|
|
||||||
method: 'Manual Print Fallback',
|
|
||||||
tabId: tab.id,
|
|
||||||
instruction: 'PDF opened in new tab. Use Ctrl+P to print or close the tab if not needed.'
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Fallback print failed:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility functions
|
|
||||||
function delay(ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension startup
|
|
||||||
console.log('🖨️ Quality Label Printing Extension loaded - Windows Service Mode');
|
|
||||||
console.log(`🔗 Service URL: ${PRINT_SERVICE_URL}`);
|
|
||||||
|
|
||||||
// Test service on startup
|
|
||||||
testServiceConnection().then(connected => {
|
|
||||||
if (connected) {
|
|
||||||
console.log('✅ Windows Print Service is available');
|
|
||||||
} else {
|
|
||||||
console.log('⚠️ Windows Print Service is not available - fallback mode active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('✅ Background script loaded successfully - Platform safe mode enabled');
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* Quality Label Printing Service - Content Script
|
|
||||||
* Simplified injection for extension ID detection
|
|
||||||
*/
|
|
||||||
|
|
||||||
console.log('Quality Label Printing - Content Script Loaded');
|
|
||||||
|
|
||||||
// Inject extension ID into DOM for web page detection
|
|
||||||
function injectExtensionId() {
|
|
||||||
// Remove any existing extension ID element
|
|
||||||
const existingElement = document.getElementById('chrome-extension-id');
|
|
||||||
if (existingElement) {
|
|
||||||
existingElement.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new element with extension ID
|
|
||||||
const extensionIdElement = document.createElement('div');
|
|
||||||
extensionIdElement.id = 'chrome-extension-id';
|
|
||||||
extensionIdElement.setAttribute('data-extension-id', chrome.runtime.id);
|
|
||||||
extensionIdElement.style.display = 'none';
|
|
||||||
|
|
||||||
// Add to document head or body
|
|
||||||
(document.head || document.body || document.documentElement).appendChild(extensionIdElement);
|
|
||||||
|
|
||||||
console.log('Extension ID injected:', chrome.runtime.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject extension ID when DOM is ready
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', injectExtensionId);
|
|
||||||
} else {
|
|
||||||
injectExtensionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also inject when page changes (for SPAs)
|
|
||||||
if (window.navigation) {
|
|
||||||
navigation.addEventListener('navigate', injectExtensionId);
|
|
||||||
} else {
|
|
||||||
// Fallback for older browsers
|
|
||||||
let lastUrl = location.href;
|
|
||||||
new MutationObserver(() => {
|
|
||||||
const url = location.href;
|
|
||||||
if (url !== lastUrl) {
|
|
||||||
lastUrl = url;
|
|
||||||
setTimeout(injectExtensionId, 100);
|
|
||||||
}
|
|
||||||
}).observe(document, { subtree: true, childList: true });
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Placeholder for icon files - in production, add actual PNG icons:
|
|
||||||
# - icon16.png (16x16 pixels)
|
|
||||||
# - icon48.png (48x48 pixels)
|
|
||||||
# - icon128.png (128x128 pixels)
|
|
||||||
|
|
||||||
# For now, create simple text-based icons using SVG converted to PNG
|
|
||||||
# These should be replaced with proper icons later
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Simple text-based icons for the Chrome extension
|
|
||||||
# These are placeholder files - replace with actual PNG icons for production
|
|
||||||
|
|
||||||
# Create simple SVG icons that can be converted to PNG
|
|
||||||
ICON_16_SVG = '''
|
|
||||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="16" height="16" fill="#007bff"/>
|
|
||||||
<text x="8" y="12" font-family="Arial" font-size="10" fill="white" text-anchor="middle">🖨</text>
|
|
||||||
</svg>
|
|
||||||
'''
|
|
||||||
|
|
||||||
ICON_48_SVG = '''
|
|
||||||
<svg width="48" height="48" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="48" height="48" fill="#007bff" rx="8"/>
|
|
||||||
<text x="24" y="32" font-family="Arial" font-size="24" fill="white" text-anchor="middle">🖨️</text>
|
|
||||||
</svg>
|
|
||||||
'''
|
|
||||||
|
|
||||||
ICON_128_SVG = '''
|
|
||||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="128" height="128" fill="#007bff" rx="16"/>
|
|
||||||
<text x="64" y="84" font-family="Arial" font-size="48" fill="white" text-anchor="middle">🖨️</text>
|
|
||||||
</svg>
|
|
||||||
'''
|
|
||||||
|
|
||||||
# For now, create simple text placeholders
|
|
||||||
# In production, convert these SVGs to PNG files
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Generate PNG icons for Chrome extension
|
|
||||||
Creates simple colored squares with printer icons
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
|
||||||
PIL_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
PIL_AVAILABLE = False
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
def create_simple_icon(size, filename):
|
|
||||||
"""Create a simple colored square icon"""
|
|
||||||
if PIL_AVAILABLE:
|
|
||||||
# Create image with PIL
|
|
||||||
img = Image.new('RGBA', (size, size), (0, 123, 255, 255)) # Blue background
|
|
||||||
draw = ImageDraw.Draw(img)
|
|
||||||
|
|
||||||
# Add a white border
|
|
||||||
border_width = max(1, size // 16)
|
|
||||||
draw.rectangle([0, 0, size-1, size-1], outline=(255, 255, 255, 255), width=border_width)
|
|
||||||
|
|
||||||
# Add text (P for Print)
|
|
||||||
try:
|
|
||||||
font_size = size // 2
|
|
||||||
font = ImageFont.load_default()
|
|
||||||
text = "P"
|
|
||||||
bbox = draw.textbbox((0, 0), text, font=font)
|
|
||||||
text_width = bbox[2] - bbox[0]
|
|
||||||
text_height = bbox[3] - bbox[1]
|
|
||||||
x = (size - text_width) // 2
|
|
||||||
y = (size - text_height) // 2
|
|
||||||
draw.text((x, y), text, fill=(255, 255, 255, 255), font=font)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
img.save(filename, 'PNG')
|
|
||||||
print(f"Created {filename} ({size}x{size})")
|
|
||||||
else:
|
|
||||||
# Create a minimal PNG file without PIL
|
|
||||||
create_minimal_png(size, filename)
|
|
||||||
|
|
||||||
def create_minimal_png(size, filename):
|
|
||||||
"""Create a minimal PNG file without PIL"""
|
|
||||||
# This creates a very basic PNG file
|
|
||||||
# Blue square with minimal PNG structure
|
|
||||||
|
|
||||||
import struct
|
|
||||||
import zlib
|
|
||||||
|
|
||||||
# PNG signature
|
|
||||||
png_signature = b'\x89PNG\r\n\x1a\n'
|
|
||||||
|
|
||||||
# IHDR chunk
|
|
||||||
width = height = size
|
|
||||||
bit_depth = 8
|
|
||||||
color_type = 2 # RGB
|
|
||||||
compression = 0
|
|
||||||
filter_method = 0
|
|
||||||
interlace = 0
|
|
||||||
|
|
||||||
ihdr_data = struct.pack('>IIBBBBB', width, height, bit_depth, color_type, compression, filter_method, interlace)
|
|
||||||
ihdr_crc = zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff
|
|
||||||
ihdr_chunk = struct.pack('>I', len(ihdr_data)) + b'IHDR' + ihdr_data + struct.pack('>I', ihdr_crc)
|
|
||||||
|
|
||||||
# IDAT chunk (blue pixels)
|
|
||||||
pixels = []
|
|
||||||
for y in range(height):
|
|
||||||
row = [0] # Filter byte
|
|
||||||
for x in range(width):
|
|
||||||
# Blue color RGB(0, 123, 255)
|
|
||||||
row.extend([0, 123, 255])
|
|
||||||
pixels.extend(row)
|
|
||||||
|
|
||||||
pixel_data = bytes(pixels)
|
|
||||||
compressed_data = zlib.compress(pixel_data)
|
|
||||||
idat_crc = zlib.crc32(b'IDAT' + compressed_data) & 0xffffffff
|
|
||||||
idat_chunk = struct.pack('>I', len(compressed_data)) + b'IDAT' + compressed_data + struct.pack('>I', idat_crc)
|
|
||||||
|
|
||||||
# IEND chunk
|
|
||||||
iend_crc = zlib.crc32(b'IEND') & 0xffffffff
|
|
||||||
iend_chunk = struct.pack('>I', 0) + b'IEND' + struct.pack('>I', iend_crc)
|
|
||||||
|
|
||||||
# Write PNG file
|
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
f.write(png_signature)
|
|
||||||
f.write(ihdr_chunk)
|
|
||||||
f.write(idat_chunk)
|
|
||||||
f.write(iend_chunk)
|
|
||||||
|
|
||||||
print(f"Created minimal {filename} ({size}x{size})")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# Create icons directory if it doesn't exist
|
|
||||||
icons_dir = "/home/ske087/quality_recticel/windows_print_service/chrome_extension/icons"
|
|
||||||
|
|
||||||
print("Creating Chrome extension icons...")
|
|
||||||
print(f"PIL available: {PIL_AVAILABLE}")
|
|
||||||
|
|
||||||
# Create required icon sizes
|
|
||||||
sizes = [16, 32, 48, 128]
|
|
||||||
|
|
||||||
for size in sizes:
|
|
||||||
filename = os.path.join(icons_dir, f"icon{size}.png")
|
|
||||||
create_simple_icon(size, filename)
|
|
||||||
|
|
||||||
print("✅ All icons created successfully!")
|
|
||||||
print("\nIcons created:")
|
|
||||||
for size in sizes:
|
|
||||||
filename = f"icon{size}.png"
|
|
||||||
filepath = os.path.join(icons_dir, filename)
|
|
||||||
if os.path.exists(filepath):
|
|
||||||
file_size = os.path.getsize(filepath)
|
|
||||||
print(f" ✓ {filename} ({size}x{size}px, {file_size} bytes)")
|
|
||||||
else:
|
|
||||||
print(f" ✗ {filename} - FAILED")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 545 B |
@@ -1,4 +0,0 @@
|
|||||||
# Placeholder for 128x128 icon
|
|
||||||
# This is a text file placeholder
|
|
||||||
# Replace with actual icon128.png file
|
|
||||||
🖨️
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 207 B |
@@ -1,4 +0,0 @@
|
|||||||
# Placeholder for 16x16 icon
|
|
||||||
# This is a text file placeholder
|
|
||||||
# Replace with actual icon16.png file
|
|
||||||
🖨️
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 235 B |
Binary file not shown.
|
Before Width: | Height: | Size: 285 B |
@@ -1,4 +0,0 @@
|
|||||||
# Placeholder for 48x48 icon
|
|
||||||
# This is a text file placeholder
|
|
||||||
# Replace with actual icon48.png file
|
|
||||||
🖨️
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
{
|
|
||||||
"manifest_version": 3,
|
|
||||||
"name": "Quality Label Printing Service",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Simple silent PDF printing for Quality Label Printing application",
|
|
||||||
|
|
||||||
"permissions": [
|
|
||||||
"activeTab",
|
|
||||||
"storage",
|
|
||||||
"downloads",
|
|
||||||
"tabs",
|
|
||||||
"scripting"
|
|
||||||
],
|
|
||||||
|
|
||||||
"host_permissions": [
|
|
||||||
"http://localhost:*/*",
|
|
||||||
"https://localhost:*/*",
|
|
||||||
"http://*/*",
|
|
||||||
"https://*/*"
|
|
||||||
],
|
|
||||||
|
|
||||||
"background": {
|
|
||||||
"service_worker": "background.js"
|
|
||||||
},
|
|
||||||
|
|
||||||
"content_scripts": [{
|
|
||||||
"matches": ["<all_urls>"],
|
|
||||||
"js": ["content.js"],
|
|
||||||
"run_at": "document_end"
|
|
||||||
}],
|
|
||||||
|
|
||||||
"action": {
|
|
||||||
"default_popup": "popup.html",
|
|
||||||
"default_title": "Quality Label Printing Service",
|
|
||||||
"default_icon": {
|
|
||||||
"16": "icons/icon16.png",
|
|
||||||
"32": "icons/icon32.png",
|
|
||||||
"48": "icons/icon48.png",
|
|
||||||
"128": "icons/icon128.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"icons": {
|
|
||||||
"16": "icons/icon16.png",
|
|
||||||
"32": "icons/icon32.png",
|
|
||||||
"48": "icons/icon48.png",
|
|
||||||
"128": "icons/icon128.png"
|
|
||||||
},
|
|
||||||
|
|
||||||
"web_accessible_resources": [{
|
|
||||||
"resources": ["content.js"],
|
|
||||||
"matches": ["<all_urls>"]
|
|
||||||
}],
|
|
||||||
|
|
||||||
"externally_connectable": {
|
|
||||||
"matches": [
|
|
||||||
"http://localhost:*/*",
|
|
||||||
"https://localhost:*/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
width: 350px;
|
|
||||||
min-height: 200px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
background: #f8f9fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
margin: 0 auto 8px;
|
|
||||||
background: #007bff;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
border-left: 4px solid #28a745;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card.error {
|
|
||||||
border-left-color: #dc3545;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card.warning {
|
|
||||||
border-left-color: #ffc107;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-title {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-message {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-detail {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
background: #0056b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:disabled {
|
|
||||||
background: #6c757d;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.secondary {
|
|
||||||
background: #6c757d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.secondary:hover {
|
|
||||||
background: #545b62;
|
|
||||||
}
|
|
||||||
|
|
||||||
.printers-list {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 12px;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.printer-item {
|
|
||||||
padding: 8px 0;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.printer-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.printer-name {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.printer-details {
|
|
||||||
color: #666;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
padding-top: 16px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #007bff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="header">
|
|
||||||
<div class="logo">QR</div>
|
|
||||||
<div class="title">Quality Label Printing Service</div>
|
|
||||||
<div class="version">Version 1.0.0</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="loading" class="loading">
|
|
||||||
<div>Checking service status...</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="content" class="hidden">
|
|
||||||
<div id="service-status" class="status-card">
|
|
||||||
<div class="status-title">Print Service Status</div>
|
|
||||||
<div class="status-message" id="status-message">Checking...</div>
|
|
||||||
<div class="status-detail" id="status-detail"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button id="refresh-btn" class="button">Refresh Status</button>
|
|
||||||
<button id="test-print-btn" class="button">Test Print</button>
|
|
||||||
<button id="get-printers-btn" class="button secondary">Get Printers</button>
|
|
||||||
|
|
||||||
<div id="printers-container" class="hidden">
|
|
||||||
<div class="printers-list">
|
|
||||||
<div style="font-weight: 600; margin-bottom: 8px; font-size: 14px;">Available Printers</div>
|
|
||||||
<div id="printers-list"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<a href="#" id="help-link" class="footer-link">Help & Documentation</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="popup.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
/**
|
|
||||||
* Quality Label Printing Service - Popup Script
|
|
||||||
* Manages the extension popup interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
console.log('Popup loaded');
|
|
||||||
|
|
||||||
// Get elements
|
|
||||||
const elements = {
|
|
||||||
loading: document.getElementById('loading'),
|
|
||||||
content: document.getElementById('content'),
|
|
||||||
serviceStatus: document.getElementById('service-status'),
|
|
||||||
statusMessage: document.getElementById('status-message'),
|
|
||||||
statusDetail: document.getElementById('status-detail'),
|
|
||||||
refreshBtn: document.getElementById('refresh-btn'),
|
|
||||||
testPrintBtn: document.getElementById('test-print-btn'),
|
|
||||||
getPrintersBtn: document.getElementById('get-printers-btn'),
|
|
||||||
printersContainer: document.getElementById('printers-container'),
|
|
||||||
printersList: document.getElementById('printers-list'),
|
|
||||||
helpLink: document.getElementById('help-link')
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize popup
|
|
||||||
await initializePopup();
|
|
||||||
|
|
||||||
// Event listeners
|
|
||||||
elements.refreshBtn.addEventListener('click', checkServiceStatus);
|
|
||||||
elements.testPrintBtn.addEventListener('click', testPrint);
|
|
||||||
elements.getPrintersBtn.addEventListener('click', getPrinters);
|
|
||||||
elements.helpLink.addEventListener('click', showHelp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize popup
|
|
||||||
*/
|
|
||||||
async function initializePopup() {
|
|
||||||
try {
|
|
||||||
await checkServiceStatus();
|
|
||||||
|
|
||||||
// Show content, hide loading
|
|
||||||
elements.loading.classList.add('hidden');
|
|
||||||
elements.content.classList.remove('hidden');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Initialization error:', error);
|
|
||||||
showError('Failed to initialize popup');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check service status
|
|
||||||
*/
|
|
||||||
async function checkServiceStatus() {
|
|
||||||
try {
|
|
||||||
elements.refreshBtn.disabled = true;
|
|
||||||
elements.refreshBtn.textContent = 'Checking...';
|
|
||||||
|
|
||||||
// Send message to background script
|
|
||||||
const result = await new Promise((resolve) => {
|
|
||||||
chrome.runtime.sendMessage({ action: 'check_service' }, resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
updateServiceStatus(result);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Service check error:', error);
|
|
||||||
showError('Failed to check service status');
|
|
||||||
} finally {
|
|
||||||
elements.refreshBtn.disabled = false;
|
|
||||||
elements.refreshBtn.textContent = 'Refresh Status';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update service status display
|
|
||||||
*/
|
|
||||||
function updateServiceStatus(result) {
|
|
||||||
const statusCard = elements.serviceStatus;
|
|
||||||
const message = elements.statusMessage;
|
|
||||||
const detail = elements.statusDetail;
|
|
||||||
|
|
||||||
if (result && result.success) {
|
|
||||||
// Service is available
|
|
||||||
statusCard.className = 'status-card';
|
|
||||||
message.textContent = 'Service is running normally';
|
|
||||||
detail.textContent = `Last checked: ${new Date().toLocaleTimeString()}`;
|
|
||||||
|
|
||||||
// Enable buttons
|
|
||||||
elements.testPrintBtn.disabled = false;
|
|
||||||
elements.getPrintersBtn.disabled = false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Service is not available
|
|
||||||
statusCard.className = 'status-card error';
|
|
||||||
message.textContent = 'Service is not available';
|
|
||||||
detail.textContent = result ? result.error : 'Unknown error';
|
|
||||||
|
|
||||||
// Disable buttons
|
|
||||||
elements.testPrintBtn.disabled = true;
|
|
||||||
elements.getPrintersBtn.disabled = false; // Keep enabled for diagnostics
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test print functionality
|
|
||||||
*/
|
|
||||||
async function testPrint() {
|
|
||||||
try {
|
|
||||||
elements.testPrintBtn.disabled = true;
|
|
||||||
elements.testPrintBtn.textContent = 'Testing...';
|
|
||||||
|
|
||||||
// Create test PDF data
|
|
||||||
const testPrintData = {
|
|
||||||
pdf_url: 'data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PAovVGl0bGUgKFRlc3QgUGFnZSkKL0NyZWF0b3IgKFF1YWxpdHkgUmVjdGljZWwgUHJpbnQgU2VydmljZSkKL1Byb2R1Y2VyIChRdWFsaXR5IFJlY3RpY2VsKQovQ3JlYXRpb25EYXRlIChEOjIwMjMwMTAxMTIwMDAwKQo+PgplbmRvYmoKMiAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMyAwIFIKPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFs0IDAgUl0KL0NvdW50IDEKPJ4KZW5kb2JqCjQgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCAzIDAgUgovTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQovQ29udGVudHMgNSAwIFIKL1Jlc291cmNlcyA8PAovRm9udCA8PAovRjEgNiAwIFIKPj4KPj4KPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0xlbmd0aCA0NAo+PgpzdHJlYW0KQlQKL0YxIDEyIFRmCjEwMCA3MDAgVGQKKFRlc3QgUHJpbnQgUGFnZSkgVGoKRVQKZW5kc3RyZWFtCmVuZG9iago2IDAgb2JqCjw8Ci9UeXBlIC9Gb250Ci9TdWJ0eXBlIC9UeXBlMQovQmFzZUZvbnQgL0hlbHZldGljYQo+PgplbmRvYmoKeHJlZgowIDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAxNDggMDAwMDAgbiAKMDAwMDAwMDE5NSAwMDAwMCBuIAowMDAwMDAwMjUyIDAwMDAwIG4gCjAwMDAwMDA0MTQgMDAwMDAgbiAKMDAwMDAwMDUwOCAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9TaXplIDcKL1Jvb3QgMiAwIFIKL0luZm8gMSAwIFIKPj4Kc3RhcnR4cmVmCjU2NQolJUVPRgo=',
|
|
||||||
printer_name: 'default',
|
|
||||||
copies: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send test print request
|
|
||||||
const result = await new Promise((resolve) => {
|
|
||||||
chrome.runtime.sendMessage({
|
|
||||||
action: 'print_pdf',
|
|
||||||
data: testPrintData
|
|
||||||
}, resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && result.success) {
|
|
||||||
showSuccess('Test print sent successfully');
|
|
||||||
} else {
|
|
||||||
showError(result ? result.error : 'Test print failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Test print error:', error);
|
|
||||||
showError('Test print failed: ' + error.message);
|
|
||||||
} finally {
|
|
||||||
elements.testPrintBtn.disabled = false;
|
|
||||||
elements.testPrintBtn.textContent = 'Test Print';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available printers
|
|
||||||
*/
|
|
||||||
async function getPrinters() {
|
|
||||||
try {
|
|
||||||
elements.getPrintersBtn.disabled = true;
|
|
||||||
elements.getPrintersBtn.textContent = 'Loading...';
|
|
||||||
|
|
||||||
// Get printers from background script
|
|
||||||
const result = await new Promise((resolve) => {
|
|
||||||
chrome.runtime.sendMessage({ action: 'get_printers' }, resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && result.success && result.printers) {
|
|
||||||
displayPrinters(result.printers);
|
|
||||||
} else {
|
|
||||||
showError(result ? result.error : 'Failed to get printers');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Get printers error:', error);
|
|
||||||
showError('Failed to get printers: ' + error.message);
|
|
||||||
} finally {
|
|
||||||
elements.getPrintersBtn.disabled = false;
|
|
||||||
elements.getPrintersBtn.textContent = 'Get Printers';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display printers list
|
|
||||||
*/
|
|
||||||
function displayPrinters(printers) {
|
|
||||||
if (!printers || printers.length === 0) {
|
|
||||||
elements.printersList.innerHTML = '<div style="color: #666; font-style: italic;">No printers found</div>';
|
|
||||||
} else {
|
|
||||||
elements.printersList.innerHTML = printers.map(printer => `
|
|
||||||
<div class="printer-item">
|
|
||||||
<div class="printer-name">${escapeHtml(printer.name)}</div>
|
|
||||||
<div class="printer-details">
|
|
||||||
Driver: ${escapeHtml(printer.driver || 'Unknown')}<br>
|
|
||||||
Port: ${escapeHtml(printer.port || 'Unknown')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
elements.printersContainer.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show success message
|
|
||||||
*/
|
|
||||||
function showSuccess(message) {
|
|
||||||
const statusCard = elements.serviceStatus;
|
|
||||||
const statusMessage = elements.statusMessage;
|
|
||||||
const statusDetail = elements.statusDetail;
|
|
||||||
|
|
||||||
statusCard.className = 'status-card';
|
|
||||||
statusMessage.textContent = message;
|
|
||||||
statusDetail.textContent = new Date().toLocaleTimeString();
|
|
||||||
|
|
||||||
// Reset after 3 seconds
|
|
||||||
setTimeout(() => {
|
|
||||||
checkServiceStatus();
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show error message
|
|
||||||
*/
|
|
||||||
function showError(message) {
|
|
||||||
const statusCard = elements.serviceStatus;
|
|
||||||
const statusMessage = elements.statusMessage;
|
|
||||||
const statusDetail = elements.statusDetail;
|
|
||||||
|
|
||||||
statusCard.className = 'status-card error';
|
|
||||||
statusMessage.textContent = 'Error: ' + message;
|
|
||||||
statusDetail.textContent = new Date().toLocaleTimeString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show help information
|
|
||||||
*/
|
|
||||||
function showHelp() {
|
|
||||||
const helpText = `
|
|
||||||
Quality Label Printing Service Help
|
|
||||||
==================================
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
1. Install the Windows Print Service using install_service.bat
|
|
||||||
2. Install this Chrome extension
|
|
||||||
3. Configure your application to use localhost:8765
|
|
||||||
|
|
||||||
API Endpoints:
|
|
||||||
• http://localhost:8765/health - Service health check
|
|
||||||
• http://localhost:8765/print/pdf - Print PDF files
|
|
||||||
• http://localhost:8765/print/silent - Silent printing
|
|
||||||
• http://localhost:8765/printers - Get available printers
|
|
||||||
|
|
||||||
Troubleshooting:
|
|
||||||
• Ensure Windows service is running
|
|
||||||
• Check firewall settings (port 8765)
|
|
||||||
• Verify Chrome extension permissions
|
|
||||||
• Check service logs: print_service.log
|
|
||||||
|
|
||||||
For support, contact the Quality Label Printing development team.
|
|
||||||
`;
|
|
||||||
|
|
||||||
alert(helpText.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape HTML to prevent XSS
|
|
||||||
*/
|
|
||||||
function escapeHtml(text) {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script to create a completely self-contained Windows Print Service package
|
|
||||||
with embedded Python distribution and comprehensive Error 1053 fixes
|
|
||||||
- Zero external dependencies required!
|
|
||||||
- Multiple installation methods with automatic fallback
|
|
||||||
- Complete Windows Service Error 1053 resolution
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import zipfile
|
|
||||||
import urllib.request
|
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Python embeddable version details
|
|
||||||
PYTHON_VERSION = "3.11.9"
|
|
||||||
PYTHON_DOWNLOAD_URL = f"https://www.python.org/ftp/python/{PYTHON_VERSION}/python-{PYTHON_VERSION}-embed-amd64.zip"
|
|
||||||
PYTHON_DIR_NAME = "python_embedded"
|
|
||||||
|
|
||||||
def download_portable_python(temp_dir):
|
|
||||||
"""Download and extract Python embedded distribution"""
|
|
||||||
print(f"📥 Downloading Python {PYTHON_VERSION} embedded distribution...")
|
|
||||||
|
|
||||||
python_zip_path = os.path.join(temp_dir, "python-embed.zip")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Download Python embedded
|
|
||||||
urllib.request.urlretrieve(PYTHON_DOWNLOAD_URL, python_zip_path)
|
|
||||||
print(f"✅ Downloaded Python to: {python_zip_path}")
|
|
||||||
|
|
||||||
# Create extraction directory
|
|
||||||
python_extract_dir = os.path.join(temp_dir, PYTHON_DIR_NAME)
|
|
||||||
os.makedirs(python_extract_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# Extract Python
|
|
||||||
with zipfile.ZipFile(python_zip_path, 'r') as zip_ref:
|
|
||||||
zip_ref.extractall(python_extract_dir)
|
|
||||||
|
|
||||||
print(f"✅ Extracted Python to: {python_extract_dir}")
|
|
||||||
|
|
||||||
# Enable site-packages by modifying pth file
|
|
||||||
pth_files = [f for f in os.listdir(python_extract_dir) if f.endswith('._pth')]
|
|
||||||
if pth_files:
|
|
||||||
pth_file = os.path.join(python_extract_dir, pth_files[0])
|
|
||||||
with open(pth_file, 'a') as f:
|
|
||||||
f.write('\nimport site\n')
|
|
||||||
print("✅ Enabled site-packages in embedded Python")
|
|
||||||
|
|
||||||
return python_extract_dir
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Failed to download Python: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def create_complete_package():
|
|
||||||
"""Create complete self-contained package with embedded Python"""
|
|
||||||
|
|
||||||
# Get current directory (should be windows_print_service)
|
|
||||||
current_dir = Path(__file__).parent
|
|
||||||
print(f"📂 Working from: {current_dir}")
|
|
||||||
|
|
||||||
# Create temporary directory
|
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
|
||||||
print(f"🔧 Using temporary directory: {temp_dir}")
|
|
||||||
|
|
||||||
# Download and extract portable Python
|
|
||||||
python_dir = download_portable_python(temp_dir)
|
|
||||||
if not python_dir:
|
|
||||||
print("❌ Failed to prepare Python distribution")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Create the complete package
|
|
||||||
package_path = current_dir / "QualityPrintService_COMPLETE_ZERO_DEPENDENCIES.zip"
|
|
||||||
|
|
||||||
print(f"📦 Creating complete package: {package_path}")
|
|
||||||
|
|
||||||
with zipfile.ZipFile(package_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
|
||||||
files_added = 0
|
|
||||||
|
|
||||||
# Add Python embedded distribution
|
|
||||||
print("📁 Adding Python embedded distribution...")
|
|
||||||
for root, dirs, files in os.walk(python_dir):
|
|
||||||
for file in files:
|
|
||||||
file_path = os.path.join(root, file)
|
|
||||||
arcname = os.path.join(PYTHON_DIR_NAME, os.path.relpath(file_path, python_dir))
|
|
||||||
zipf.write(file_path, arcname)
|
|
||||||
files_added += 1
|
|
||||||
|
|
||||||
# Add all service files
|
|
||||||
print("📁 Adding service files...")
|
|
||||||
service_files = [
|
|
||||||
"print_service_complete.py",
|
|
||||||
"service_wrapper.py",
|
|
||||||
"service_installer.py",
|
|
||||||
"install_service_complete.bat",
|
|
||||||
"install_service_ENHANCED.bat",
|
|
||||||
"uninstall_service_complete.bat",
|
|
||||||
"fix_error_1053.bat",
|
|
||||||
"test_service.bat",
|
|
||||||
"build_executable.bat",
|
|
||||||
"build_package.py",
|
|
||||||
"create_portable_package.py",
|
|
||||||
"requirements_complete.txt",
|
|
||||||
"INSTALLATION_COMPLETE.md",
|
|
||||||
"PACKAGE_SUMMARY.md",
|
|
||||||
"README_COMPLETE.md",
|
|
||||||
"TROUBLESHOOTING_1053.md",
|
|
||||||
"ERROR_1053_COMPLETE_FIX.md",
|
|
||||||
"PORTABLE_PYTHON_INSTRUCTIONS.txt"
|
|
||||||
]
|
|
||||||
|
|
||||||
for file_name in service_files:
|
|
||||||
file_path = current_dir / file_name
|
|
||||||
if file_path.exists():
|
|
||||||
zipf.write(file_path, file_name)
|
|
||||||
files_added += 1
|
|
||||||
print(f" ✅ Added: {file_name}")
|
|
||||||
|
|
||||||
# Add Chrome extension
|
|
||||||
chrome_ext_dir = current_dir / "chrome_extension"
|
|
||||||
if chrome_ext_dir.exists():
|
|
||||||
print("📁 Adding Chrome extension...")
|
|
||||||
for root, dirs, files in os.walk(chrome_ext_dir):
|
|
||||||
for file in files:
|
|
||||||
if not file.startswith('.'):
|
|
||||||
file_path = os.path.join(root, file)
|
|
||||||
arcname = os.path.relpath(file_path, current_dir)
|
|
||||||
zipf.write(file_path, arcname)
|
|
||||||
files_added += 1
|
|
||||||
|
|
||||||
# Create updated installer that uses embedded Python
|
|
||||||
updated_installer = f'''@echo off
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
echo ========================================
|
|
||||||
echo Quality Label Print Service Installer
|
|
||||||
echo ZERO DEPENDENCIES - COMPLETE PACKAGE
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo This package includes EVERYTHING needed:
|
|
||||||
echo ✅ Embedded Python {PYTHON_VERSION}
|
|
||||||
echo ✅ Complete Print Service
|
|
||||||
echo ✅ Windows Service Installer
|
|
||||||
echo ✅ Chrome Extension
|
|
||||||
echo ✅ Auto-recovery System
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check for administrator privileges
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ❌ ERROR: Administrator privileges required
|
|
||||||
echo Please right-click this file and select "Run as administrator"
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [1/7] Administrator privileges confirmed ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Set variables
|
|
||||||
set CURRENT_DIR=%~dp0
|
|
||||||
set SERVICE_NAME=QualityPrintService
|
|
||||||
set SERVICE_DISPLAY_NAME=Quality Label Print Service
|
|
||||||
set INSTALL_DIR=C:\\QualityPrintService
|
|
||||||
set PYTHON_EXE=%INSTALL_DIR%\\{PYTHON_DIR_NAME}\\python.exe
|
|
||||||
set PYTHON_SCRIPT=%INSTALL_DIR%\\print_service_complete.py
|
|
||||||
set LOG_DIR=%USERPROFILE%\\PrintService\\logs
|
|
||||||
|
|
||||||
echo [2/7] Using embedded Python distribution ✅
|
|
||||||
echo Python location: %PYTHON_EXE%
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Stop existing service if running
|
|
||||||
echo [3/7] Stopping existing service (if any)...
|
|
||||||
sc query "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Stopping existing service...
|
|
||||||
net stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
sc delete "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
timeout /t 2 >nul
|
|
||||||
)
|
|
||||||
echo Service cleanup completed ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Create installation directory
|
|
||||||
echo [4/7] Creating installation directory...
|
|
||||||
if exist "%INSTALL_DIR%" (
|
|
||||||
echo Removing old installation...
|
|
||||||
rmdir /s /q "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
)
|
|
||||||
mkdir "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
echo Installation directory: %INSTALL_DIR% ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Copy all files to installation directory
|
|
||||||
echo [5/7] Installing service files...
|
|
||||||
echo Copying embedded Python...
|
|
||||||
xcopy "%CURRENT_DIR%{PYTHON_DIR_NAME}" "%INSTALL_DIR%\\{PYTHON_DIR_NAME}\\" /E /I /Y >nul
|
|
||||||
echo Copying service script...
|
|
||||||
copy "%CURRENT_DIR%print_service_complete.py" "%INSTALL_DIR%\\" >nul
|
|
||||||
echo Service files installed ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Create log directory
|
|
||||||
echo [6/7] Setting up logging...
|
|
||||||
mkdir "%LOG_DIR%" >nul 2>&1
|
|
||||||
echo Log directory: %LOG_DIR% ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Install and start Windows service
|
|
||||||
echo [7/7] Installing Windows service...
|
|
||||||
sc create "%SERVICE_NAME%" binPath= "\\"%PYTHON_EXE%\\" \\"%PYTHON_SCRIPT%\\"" DisplayName= "%SERVICE_DISPLAY_NAME%" start= auto
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ❌ Failed to create Windows service
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Configure service recovery
|
|
||||||
sc failure "%SERVICE_NAME%" reset= 60 actions= restart/10000/restart/30000/restart/60000
|
|
||||||
sc config "%SERVICE_NAME%" depend= ""
|
|
||||||
|
|
||||||
REM Start the service
|
|
||||||
echo Starting service...
|
|
||||||
net start "%SERVICE_NAME%"
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ⚠️ Service created but failed to start immediately
|
|
||||||
echo This is normal - the service will start automatically on reboot
|
|
||||||
) else (
|
|
||||||
echo Service started successfully ✅
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo INSTALLATION COMPLETED! 🎉
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo ✅ Python embedded distribution installed
|
|
||||||
echo ✅ Windows Print Service installed and configured
|
|
||||||
echo ✅ Auto-recovery enabled (restarts on failure)
|
|
||||||
echo ✅ Service will start automatically on boot
|
|
||||||
echo.
|
|
||||||
echo 🌐 Service URL: http://localhost:8765
|
|
||||||
echo 📊 Health check: http://localhost:8765/health
|
|
||||||
echo 📁 Logs location: %LOG_DIR%
|
|
||||||
echo.
|
|
||||||
echo 📋 NEXT STEPS:
|
|
||||||
echo 1. Install Chrome extension from 'chrome_extension' folder
|
|
||||||
echo 2. Test service: http://localhost:8765/health
|
|
||||||
echo 3. Configure web application to use service
|
|
||||||
echo.
|
|
||||||
echo Press any key to test service connection...
|
|
||||||
pause >nul
|
|
||||||
|
|
||||||
REM Test service
|
|
||||||
echo Testing service connection...
|
|
||||||
timeout /t 3 >nul
|
|
||||||
curl -s http://localhost:8765/health >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ✅ Service is responding correctly!
|
|
||||||
) else (
|
|
||||||
echo ⚠️ Service test failed - may need a moment to start
|
|
||||||
echo Check logs in: %LOG_DIR%
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Installation complete! Service is ready to use.
|
|
||||||
pause
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Add the updated installer
|
|
||||||
zipf.writestr("install_service_ZERO_DEPENDENCIES.bat", updated_installer)
|
|
||||||
files_added += 1
|
|
||||||
|
|
||||||
# Add comprehensive README
|
|
||||||
readme_content = f'''# Quality Print Service - ZERO DEPENDENCIES Package
|
|
||||||
|
|
||||||
## 🎯 COMPLETE SELF-CONTAINED INSTALLATION
|
|
||||||
|
|
||||||
This package contains EVERYTHING needed to run the Quality Print Service:
|
|
||||||
|
|
||||||
### ✅ What's Included:
|
|
||||||
- **Embedded Python {PYTHON_VERSION}** - No system Python required!
|
|
||||||
- **Complete Print Service** - Zero external dependencies
|
|
||||||
- **Windows Service Installer** - Automatic installation and recovery
|
|
||||||
- **Chrome Extension** - Web browser integration
|
|
||||||
- **Comprehensive Documentation** - Installation and usage guides
|
|
||||||
|
|
||||||
### 🚀 INSTALLATION (5 Minutes):
|
|
||||||
|
|
||||||
#### Requirements:
|
|
||||||
- Windows 10/11 or Windows Server 2016+
|
|
||||||
- Administrator privileges (for service installation)
|
|
||||||
- Google Chrome browser
|
|
||||||
|
|
||||||
#### Step 1: Extract Package
|
|
||||||
- Extract this ZIP file to any location (Desktop, Downloads, etc.)
|
|
||||||
- No permanent location needed - installer copies files automatically
|
|
||||||
|
|
||||||
#### Step 2: Install Service
|
|
||||||
- Right-click `install_service_ZERO_DEPENDENCIES.bat`
|
|
||||||
- Select "Run as administrator"
|
|
||||||
- Follow the installation prompts
|
|
||||||
|
|
||||||
#### Step 3: Install Chrome Extension
|
|
||||||
- Open Chrome browser
|
|
||||||
- Navigate to `chrome://extensions/`
|
|
||||||
- Enable "Developer mode" (toggle in top-right)
|
|
||||||
- Click "Load unpacked"
|
|
||||||
- Select the `chrome_extension` folder from extracted package
|
|
||||||
|
|
||||||
#### Step 4: Test Installation
|
|
||||||
- Visit: http://localhost:8765/health
|
|
||||||
- Should return: {{"status": "healthy", "service": "Windows Print Service"}}
|
|
||||||
|
|
||||||
### 🔧 Technical Details:
|
|
||||||
|
|
||||||
**Service Architecture:**
|
|
||||||
```
|
|
||||||
Quality Web App → Chrome Extension → Windows Service → Printer
|
|
||||||
```
|
|
||||||
|
|
||||||
**Printing Methods (automatic fallback):**
|
|
||||||
1. Adobe Reader (silent printing)
|
|
||||||
2. SumatraPDF (if Adobe unavailable)
|
|
||||||
3. PowerShell Print-Document
|
|
||||||
4. Microsoft Edge (fallback)
|
|
||||||
5. Windows default printer
|
|
||||||
|
|
||||||
**Service Management:**
|
|
||||||
- Automatic startup on Windows boot
|
|
||||||
- Auto-recovery on failure (3 restart attempts)
|
|
||||||
- Comprehensive logging in: `%USERPROFILE%\\PrintService\\logs\\`
|
|
||||||
|
|
||||||
**Network Configuration:**
|
|
||||||
- Service runs on: http://localhost:8765
|
|
||||||
- Chrome extension communicates via this local endpoint
|
|
||||||
- No external network access required
|
|
||||||
|
|
||||||
### 📂 Package Contents:
|
|
||||||
```
|
|
||||||
QualityPrintService_COMPLETE_ZERO_DEPENDENCIES.zip
|
|
||||||
├── python_embedded/ # Python {PYTHON_VERSION} embedded
|
|
||||||
├── print_service_complete.py # Main service script
|
|
||||||
├── install_service_ZERO_DEPENDENCIES.bat # Installer
|
|
||||||
├── chrome_extension/ # Browser extension
|
|
||||||
│ ├── manifest.json
|
|
||||||
│ ├── background.js
|
|
||||||
│ ├── content.js
|
|
||||||
│ ├── popup.html
|
|
||||||
│ └── popup.js
|
|
||||||
└── README.md # This file
|
|
||||||
```
|
|
||||||
|
|
||||||
### 🛠️ Troubleshooting:
|
|
||||||
|
|
||||||
**Service Won't Start:**
|
|
||||||
1. Check Windows Event Viewer → Windows Logs → Application
|
|
||||||
2. Check service logs in: `%USERPROFILE%\\PrintService\\logs\\`
|
|
||||||
3. Verify Python embedded installation in: `C:\\QualityPrintService\\`
|
|
||||||
|
|
||||||
**Chrome Extension Not Working:**
|
|
||||||
1. Verify extension is enabled in `chrome://extensions/`
|
|
||||||
2. Check browser console for error messages
|
|
||||||
3. Ensure service is running: http://localhost:8765/health
|
|
||||||
|
|
||||||
**Printing Issues:**
|
|
||||||
1. Verify printer is installed and accessible
|
|
||||||
2. Check service logs for printing attempts
|
|
||||||
3. Test manual PDF printing to verify printer functionality
|
|
||||||
|
|
||||||
### 🔄 Uninstallation:
|
|
||||||
```cmd
|
|
||||||
# Stop and remove service
|
|
||||||
sc stop QualityPrintService
|
|
||||||
sc delete QualityPrintService
|
|
||||||
|
|
||||||
# Remove installation directory
|
|
||||||
rmdir /s /q C:\\QualityPrintService
|
|
||||||
|
|
||||||
# Remove Chrome extension manually from chrome://extensions/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 📞 Support:
|
|
||||||
- Service logs: `%USERPROFILE%\\PrintService\\logs\\service.log`
|
|
||||||
- Health check: http://localhost:8765/health
|
|
||||||
- Printer list: http://localhost:8765/printers
|
|
||||||
|
|
||||||
---
|
|
||||||
**Package Version:** Complete Zero Dependencies
|
|
||||||
**Python Version:** {PYTHON_VERSION} (Embedded)
|
|
||||||
**Created:** {os.path.basename(__file__)}
|
|
||||||
'''
|
|
||||||
|
|
||||||
zipf.writestr("README_ZERO_DEPENDENCIES.md", readme_content)
|
|
||||||
files_added += 1
|
|
||||||
|
|
||||||
print(f"\n📦 Enhanced Package created successfully!")
|
|
||||||
print(f"📄 Total files: {files_added}")
|
|
||||||
print(f"📂 Location: {package_path}")
|
|
||||||
print(f"📏 Size: {package_path.stat().st_size / 1024 / 1024:.1f} MB")
|
|
||||||
print(f"🔧 Features: Error 1053 fixes, multiple installation methods")
|
|
||||||
print(f"🚀 Python: {PYTHON_VERSION} embedded (zero dependencies)")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🚀 Creating Enhanced Package with Error 1053 Fixes...")
|
|
||||||
print("=" * 65)
|
|
||||||
|
|
||||||
if create_complete_package():
|
|
||||||
print("\n✅ SUCCESS: Enhanced package created with Error 1053 fixes!")
|
|
||||||
print("\n📋 Next steps:")
|
|
||||||
print("1. Package includes multiple installation methods")
|
|
||||||
print("2. Error 1053 diagnostic and fix tools included")
|
|
||||||
print("3. Test on Windows system - should resolve all service issues")
|
|
||||||
print("4. Update Flask app to serve this enhanced package")
|
|
||||||
else:
|
|
||||||
print("\n❌ FAILED: Package creation failed")
|
|
||||||
sys.exit(1)
|
|
||||||
@@ -1,454 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM Windows Service Error 1053 - COMPREHENSIVE DIAGNOSTIC TOOL
|
|
||||||
REM This script diagnoses and fixes the most common causes of Error 1053
|
|
||||||
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
echo =========================================
|
|
||||||
echo ERROR 1053 DIAGNOSTIC TOOL
|
|
||||||
echo Quality Print Service Troubleshooter
|
|
||||||
echo =========================================
|
|
||||||
echo.
|
|
||||||
echo This tool diagnoses and fixes Windows Service Error 1053:
|
|
||||||
echo "The service did not respond to the start or control request in a timely fashion"
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check for administrator privileges
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ❌ CRITICAL: Administrator privileges required
|
|
||||||
echo Right-click this file and select "Run as administrator"
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ✅ Administrator privileges confirmed
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Set variables
|
|
||||||
set CURRENT_DIR=%~dp0
|
|
||||||
set SERVICE_NAME=QualityPrintService
|
|
||||||
set INSTALL_DIR=C:\QualityPrintService
|
|
||||||
set LOG_DIR=%USERPROFILE%\PrintService\logs
|
|
||||||
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 1] SERVICE STATUS DIAGNOSIS
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
REM Check current service status
|
|
||||||
echo Checking service status...
|
|
||||||
sc query "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Service exists - checking status:
|
|
||||||
sc query "%SERVICE_NAME%"
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Get detailed service info
|
|
||||||
echo Service configuration:
|
|
||||||
sc qc "%SERVICE_NAME%"
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo ❌ Service not found - needs installation
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check Task Scheduler
|
|
||||||
echo Checking Task Scheduler...
|
|
||||||
schtasks /query /tn "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ✅ Task Scheduler entry found
|
|
||||||
schtasks /query /tn "%SERVICE_NAME%" /fo LIST
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo ❌ Task Scheduler entry not found
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 2] PROCESS AND PORT DIAGNOSIS
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
REM Check for running processes
|
|
||||||
echo Checking for existing service processes...
|
|
||||||
tasklist /fi "imagename eq python.exe" | findstr python.exe >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Python processes found:
|
|
||||||
tasklist /fi "imagename eq python.exe"
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo No Python processes running
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check port 8765
|
|
||||||
echo Checking port 8765...
|
|
||||||
netstat -an | findstr :8765 >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ⚠️ Port 8765 is in use:
|
|
||||||
netstat -an | findstr :8765
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Find process using port
|
|
||||||
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :8765') do (
|
|
||||||
tasklist /fi "pid eq %%a" 2>nul | findstr /v "INFO:"
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo ✅ Port 8765 is available
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 3] PYTHON ENVIRONMENT DIAGNOSIS
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
REM Check Python installations
|
|
||||||
echo Checking Python installations...
|
|
||||||
|
|
||||||
REM Check embedded Python
|
|
||||||
if exist "%INSTALL_DIR%\python_embedded\python.exe" (
|
|
||||||
echo ✅ Embedded Python found: %INSTALL_DIR%\python_embedded\python.exe
|
|
||||||
"%INSTALL_DIR%\python_embedded\python.exe" --version 2>nul
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo ❌ Embedded Python not found at %INSTALL_DIR%\python_embedded\python.exe
|
|
||||||
)
|
|
||||||
|
|
||||||
if exist "%CURRENT_DIR%python_embedded\python.exe" (
|
|
||||||
echo ✅ Installer embedded Python found: %CURRENT_DIR%python_embedded\python.exe
|
|
||||||
"%CURRENT_DIR%python_embedded\python.exe" --version 2>nul
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo ❌ Installer embedded Python not found
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check system Python
|
|
||||||
python --version >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ✅ System Python found:
|
|
||||||
python --version
|
|
||||||
where python
|
|
||||||
echo.
|
|
||||||
) else (
|
|
||||||
echo ❌ System Python not found in PATH
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 4] FILE SYSTEM DIAGNOSIS
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
REM Check installation files
|
|
||||||
echo Checking installation files...
|
|
||||||
|
|
||||||
if exist "%INSTALL_DIR%" (
|
|
||||||
echo ✅ Installation directory exists: %INSTALL_DIR%
|
|
||||||
echo Contents:
|
|
||||||
dir "%INSTALL_DIR%" /b
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if exist "%INSTALL_DIR%\print_service_complete.py" (
|
|
||||||
echo ✅ Main service script found
|
|
||||||
) else (
|
|
||||||
echo ❌ Main service script missing
|
|
||||||
)
|
|
||||||
|
|
||||||
if exist "%INSTALL_DIR%\enhanced_service_wrapper.bat" (
|
|
||||||
echo ✅ Service wrapper found
|
|
||||||
) else (
|
|
||||||
echo ❌ Service wrapper missing
|
|
||||||
)
|
|
||||||
|
|
||||||
) else (
|
|
||||||
echo ❌ Installation directory not found: %INSTALL_DIR%
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check log directory
|
|
||||||
if exist "%LOG_DIR%" (
|
|
||||||
echo ✅ Log directory exists: %LOG_DIR%
|
|
||||||
if exist "%LOG_DIR%\service_wrapper.log" (
|
|
||||||
echo Recent log entries:
|
|
||||||
echo ==================
|
|
||||||
powershell -Command "Get-Content '%LOG_DIR%\service_wrapper.log' -Tail 10" 2>nul
|
|
||||||
echo ==================
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo ❌ Log directory not found: %LOG_DIR%
|
|
||||||
mkdir "%LOG_DIR%" >nul 2>&1
|
|
||||||
echo Created log directory
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 5] SERVICE TEST
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
REM Determine Python executable
|
|
||||||
set PYTHON_EXE=
|
|
||||||
if exist "%INSTALL_DIR%\python_embedded\python.exe" (
|
|
||||||
set PYTHON_EXE=%INSTALL_DIR%\python_embedded\python.exe
|
|
||||||
) else if exist "%CURRENT_DIR%python_embedded\python.exe" (
|
|
||||||
set PYTHON_EXE=%CURRENT_DIR%python_embedded\python.exe
|
|
||||||
) else (
|
|
||||||
python --version >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
set PYTHON_EXE=python
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if "!PYTHON_EXE!"=="" (
|
|
||||||
echo ❌ CRITICAL: No Python executable found
|
|
||||||
echo Cannot perform service test
|
|
||||||
goto :fixes
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Testing service with Python: !PYTHON_EXE!
|
|
||||||
|
|
||||||
REM Test service script
|
|
||||||
if exist "%INSTALL_DIR%\print_service_complete.py" (
|
|
||||||
cd /d "%INSTALL_DIR%"
|
|
||||||
|
|
||||||
echo Testing service script syntax...
|
|
||||||
"!PYTHON_EXE!" -m py_compile print_service_complete.py
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Service script syntax OK
|
|
||||||
) else (
|
|
||||||
echo ❌ Service script syntax error
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Testing service functionality...
|
|
||||||
"!PYTHON_EXE!" print_service_complete.py --test
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Service test passed
|
|
||||||
) else (
|
|
||||||
echo ❌ Service test failed
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Quick standalone test
|
|
||||||
echo Testing standalone mode (15 seconds)...
|
|
||||||
start /min "" "!PYTHON_EXE!" print_service_complete.py --standalone
|
|
||||||
|
|
||||||
timeout /t 5 >nul
|
|
||||||
|
|
||||||
REM Test connection
|
|
||||||
curl -s http://localhost:8765/health >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Service responding in standalone mode
|
|
||||||
|
|
||||||
REM Stop standalone service
|
|
||||||
taskkill /f /im python.exe >nul 2>&1
|
|
||||||
) else (
|
|
||||||
powershell -Command "try { Invoke-WebRequest -Uri 'http://localhost:8765/health' -UseBasicParsing } catch { exit 1 }" >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Service responding in standalone mode
|
|
||||||
taskkill /f /im python.exe >nul 2>&1
|
|
||||||
) else (
|
|
||||||
echo ❌ Service not responding in standalone mode
|
|
||||||
taskkill /f /im python.exe >nul 2>&1
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo ❌ Service script not found at %INSTALL_DIR%\print_service_complete.py
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:fixes
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 6] AUTOMATED FIXES FOR ERROR 1053
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
echo Applying automated fixes...
|
|
||||||
|
|
||||||
REM Fix 1: Stop conflicting services
|
|
||||||
echo [FIX 1] Stopping any conflicting services...
|
|
||||||
net stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
schtasks /end /tn "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
taskkill /f /im python.exe >nul 2>&1
|
|
||||||
timeout /t 3 >nul
|
|
||||||
echo ✅ Services stopped
|
|
||||||
|
|
||||||
REM Fix 2: Remove old service entries
|
|
||||||
echo [FIX 2] Cleaning old service entries...
|
|
||||||
sc delete "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
schtasks /delete /tn "%SERVICE_NAME%" /f >nul 2>&1
|
|
||||||
echo ✅ Old entries removed
|
|
||||||
|
|
||||||
REM Fix 3: Create enhanced service wrapper with timeout handling
|
|
||||||
echo [FIX 3] Creating enhanced service wrapper...
|
|
||||||
|
|
||||||
set ENHANCED_WRAPPER=%INSTALL_DIR%\error_1053_fix_wrapper.bat
|
|
||||||
|
|
||||||
mkdir "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
|
|
||||||
echo @echo off > "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Enhanced Windows Service Wrapper - Error 1053 Fix >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM This wrapper includes specific fixes for SCM timeout issues >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo setlocal >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Logging >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo set LOG_FILE=%LOG_DIR%\error_1053_fix.log >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo echo %%date%% %%time%% - Service wrapper starting... ^>^> "%%LOG_FILE%%" >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Change to service directory >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo cd /d "%INSTALL_DIR%" >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Pre-flight checks >> "%ENHANCED_WRAPPER%"
|
|
||||||
if "!PYTHON_EXE!" neq "" (
|
|
||||||
echo if not exist "!PYTHON_EXE!" ( >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo echo ERROR: Python not found at !PYTHON_EXE! ^>^> "%%LOG_FILE%%" >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo exit /b 1 >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo ^) >> "%ENHANCED_WRAPPER%"
|
|
||||||
)
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo if not exist "print_service_complete.py" ( >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo echo ERROR: Service script not found ^>^> "%%LOG_FILE%%" >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo exit /b 1 >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo ^) >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Quick response to SCM - Start service immediately in background >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo echo Service responding to SCM... ^>^> "%%LOG_FILE%%" >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Start the actual service process >> "%ENHANCED_WRAPPER%"
|
|
||||||
if "!PYTHON_EXE!" neq "" (
|
|
||||||
echo start /b "Quality Print Service" "!PYTHON_EXE!" "print_service_complete.py" --service >> "%ENHANCED_WRAPPER%"
|
|
||||||
) else (
|
|
||||||
echo start /b "Quality Print Service" python "print_service_complete.py" --service >> "%ENHANCED_WRAPPER%"
|
|
||||||
)
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo REM Keep wrapper alive briefly to satisfy SCM >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo timeout /t 2 /nobreak ^>nul >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo. >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo echo Service started successfully ^>^> "%%LOG_FILE%%" >> "%ENHANCED_WRAPPER%"
|
|
||||||
echo exit /b 0 >> "%ENHANCED_WRAPPER%"
|
|
||||||
|
|
||||||
echo ✅ Enhanced wrapper created
|
|
||||||
|
|
||||||
REM Fix 4: Install service with proper timeout settings
|
|
||||||
echo [FIX 4] Installing service with Error 1053 fixes...
|
|
||||||
|
|
||||||
REM Create service with extended timeout
|
|
||||||
sc create "%SERVICE_NAME%" binPath= "\"%ENHANCED_WRAPPER%\"" DisplayName= "Quality Print Service (Error 1053 Fixed)" start= auto >nul 2>&1
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ✅ Service created successfully
|
|
||||||
|
|
||||||
REM Configure service for Error 1053 prevention
|
|
||||||
echo Configuring service recovery...
|
|
||||||
sc failure "%SERVICE_NAME%" reset= 86400 actions= restart/5000/restart/5000/restart/5000 >nul 2>&1
|
|
||||||
|
|
||||||
REM Set service to auto-start with delay
|
|
||||||
sc config "%SERVICE_NAME%" start= delayed-auto >nul 2>&1
|
|
||||||
|
|
||||||
echo ✅ Service configured with Error 1053 fixes
|
|
||||||
) else (
|
|
||||||
echo ❌ Service creation failed
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 7] SERVICE START TEST
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
echo Testing service start with Error 1053 fixes...
|
|
||||||
|
|
||||||
REM Start service
|
|
||||||
net start "%SERVICE_NAME%" 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ✅ SERVICE STARTED SUCCESSFULLY!
|
|
||||||
echo Error 1053 has been FIXED! 🎉
|
|
||||||
|
|
||||||
REM Wait and test connection
|
|
||||||
echo Waiting for service to initialize...
|
|
||||||
timeout /t 10 >nul
|
|
||||||
|
|
||||||
echo Testing service connection...
|
|
||||||
curl -s http://localhost:8765/health >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Service is responding properly
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo PROBLEM SOLVED! 🎉
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo ✅ Windows Service Error 1053 FIXED
|
|
||||||
echo ✅ Service: %SERVICE_NAME%
|
|
||||||
echo ✅ Status: Running and responding
|
|
||||||
echo ✅ URL: http://localhost:8765
|
|
||||||
echo.
|
|
||||||
goto :success
|
|
||||||
) else (
|
|
||||||
powershell -Command "try { Invoke-WebRequest -Uri 'http://localhost:8765/health' -UseBasicParsing } catch { exit 1 }" >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Service is responding properly
|
|
||||||
goto :success
|
|
||||||
) else (
|
|
||||||
echo ⚠️ Service started but not responding
|
|
||||||
echo Check logs: %LOG_DIR%\error_1053_fix.log
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo ❌ Service start failed - Error 1053 may persist
|
|
||||||
echo.
|
|
||||||
echo ===========================================
|
|
||||||
echo [ALTERNATIVE SOLUTIONS]
|
|
||||||
echo ===========================================
|
|
||||||
echo.
|
|
||||||
echo Since SC Service failed, trying alternative methods...
|
|
||||||
|
|
||||||
REM Alternative: Task Scheduler
|
|
||||||
echo Installing via Task Scheduler...
|
|
||||||
schtasks /create /tn "%SERVICE_NAME%" /tr "\"%ENHANCED_WRAPPER%\"" /sc onstart /ru SYSTEM /rl HIGHEST >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Task Scheduler service created
|
|
||||||
schtasks /run /tn "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo ✅ Task Scheduler service started
|
|
||||||
echo Alternative solution: Task Scheduler
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Alternative: Manual startup
|
|
||||||
echo.
|
|
||||||
echo Manual startup option:
|
|
||||||
echo Run this command to start manually:
|
|
||||||
echo "%ENHANCED_WRAPPER%"
|
|
||||||
)
|
|
||||||
|
|
||||||
:success
|
|
||||||
echo.
|
|
||||||
echo ===========================================
|
|
||||||
echo [STEP 8] SUMMARY AND RECOMMENDATIONS
|
|
||||||
echo ===========================================
|
|
||||||
|
|
||||||
echo Diagnostic complete!
|
|
||||||
echo.
|
|
||||||
echo 📋 TROUBLESHOOTING SUMMARY:
|
|
||||||
echo ✅ Administrator privileges: OK
|
|
||||||
echo ✅ Enhanced service wrapper: Created
|
|
||||||
echo ✅ Error 1053 fixes: Applied
|
|
||||||
echo ✅ Service configuration: Updated
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if exist "%LOG_DIR%\error_1053_fix.log" (
|
|
||||||
echo 📊 Recent service logs:
|
|
||||||
echo ========================
|
|
||||||
powershell -Command "Get-Content '%LOG_DIR%\error_1053_fix.log' -Tail 5" 2>nul
|
|
||||||
echo ========================
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo 🔧 Service Management Commands:
|
|
||||||
echo - Start: net start %SERVICE_NAME%
|
|
||||||
echo - Stop: net stop %SERVICE_NAME%
|
|
||||||
echo - Status: sc query %SERVICE_NAME%
|
|
||||||
echo.
|
|
||||||
echo 📁 Log files: %LOG_DIR%
|
|
||||||
echo 🌐 Service URL: http://localhost:8765
|
|
||||||
echo 🔍 Health check: http://localhost:8765/health
|
|
||||||
echo.
|
|
||||||
echo Press any key to exit...
|
|
||||||
pause >nul
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM Windows Print Service - ENHANCED INSTALLER - Fixes Error 1053
|
|
||||||
REM This installer addresses the most common causes of Windows Service startup failures
|
|
||||||
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
echo =========================================
|
|
||||||
echo Quality Print Service - ENHANCED INSTALLER
|
|
||||||
echo Fixes Windows Service Error 1053
|
|
||||||
echo =========================================
|
|
||||||
echo.
|
|
||||||
echo This installer includes multiple methods to ensure service starts:
|
|
||||||
echo ✅ Windows SC Service (preferred)
|
|
||||||
echo ✅ Task Scheduler Service (fallback)
|
|
||||||
echo ✅ Startup Script (manual fallback)
|
|
||||||
echo ✅ Enhanced error detection and recovery
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check for administrator privileges
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ❌ ERROR: Administrator privileges required
|
|
||||||
echo Please right-click this file and select "Run as administrator"
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [1/8] Administrator privileges confirmed ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Set variables
|
|
||||||
set CURRENT_DIR=%~dp0
|
|
||||||
set SERVICE_NAME=QualityPrintService
|
|
||||||
set SERVICE_DISPLAY_NAME=Quality Print Service
|
|
||||||
set INSTALL_DIR=C:\QualityPrintService
|
|
||||||
set LOG_DIR=%USERPROFILE%\PrintService\logs
|
|
||||||
|
|
||||||
REM Detect Python executable
|
|
||||||
echo [2/8] Detecting Python installation...
|
|
||||||
|
|
||||||
set PYTHON_EXE=
|
|
||||||
set PYTHON_SCRIPT=%INSTALL_DIR%\print_service_complete.py
|
|
||||||
|
|
||||||
REM Check for embedded Python first
|
|
||||||
if exist "%INSTALL_DIR%\python_embedded\python.exe" (
|
|
||||||
set PYTHON_EXE=%INSTALL_DIR%\python_embedded\python.exe
|
|
||||||
echo Found embedded Python: !PYTHON_EXE! ✅
|
|
||||||
) else if exist "%CURRENT_DIR%python_embedded\python.exe" (
|
|
||||||
set PYTHON_EXE=%CURRENT_DIR%python_embedded\python.exe
|
|
||||||
echo Found embedded Python in installer: !PYTHON_EXE! ✅
|
|
||||||
) else (
|
|
||||||
REM Check system Python
|
|
||||||
python --version >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
set PYTHON_EXE=python
|
|
||||||
echo Found system Python ✅
|
|
||||||
) else (
|
|
||||||
echo ❌ ERROR: Python not found
|
|
||||||
echo Please ensure Python 3.7+ is installed or use the zero-dependency package
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Stop existing service
|
|
||||||
echo [3/8] Stopping existing service (if any)...
|
|
||||||
sc query "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Stopping existing service...
|
|
||||||
net stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
sc delete "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
timeout /t 3 >nul
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Stop any existing task
|
|
||||||
schtasks /end /tn "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
schtasks /delete /tn "%SERVICE_NAME%" /f >nul 2>&1
|
|
||||||
|
|
||||||
echo Service cleanup completed ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Create installation directory
|
|
||||||
echo [4/8] Creating installation directory...
|
|
||||||
if exist "%INSTALL_DIR%" (
|
|
||||||
echo Removing old installation...
|
|
||||||
rmdir /s /q "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
)
|
|
||||||
mkdir "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
|
|
||||||
REM Create log directory
|
|
||||||
mkdir "%LOG_DIR%" >nul 2>&1
|
|
||||||
|
|
||||||
echo Installation directory: %INSTALL_DIR% ✅
|
|
||||||
echo Log directory: %LOG_DIR% ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Copy service files
|
|
||||||
echo [5/8] Installing service files...
|
|
||||||
|
|
||||||
REM Copy Python embedded if available
|
|
||||||
if exist "%CURRENT_DIR%python_embedded\" (
|
|
||||||
echo Copying embedded Python distribution...
|
|
||||||
xcopy "%CURRENT_DIR%python_embedded" "%INSTALL_DIR%\python_embedded\" /E /I /Y >nul
|
|
||||||
set PYTHON_EXE=%INSTALL_DIR%\python_embedded\python.exe
|
|
||||||
echo Embedded Python installed ✅
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Copy service scripts
|
|
||||||
copy "%CURRENT_DIR%print_service_complete.py" "%INSTALL_DIR%\" >nul
|
|
||||||
copy "%CURRENT_DIR%service_wrapper.py" "%INSTALL_DIR%\" >nul 2>&1
|
|
||||||
copy "%CURRENT_DIR%service_installer.py" "%INSTALL_DIR%\" >nul 2>&1
|
|
||||||
|
|
||||||
echo Service files installed ✅
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Test service before installing
|
|
||||||
echo [6/8] Testing service functionality...
|
|
||||||
cd /d "%INSTALL_DIR%"
|
|
||||||
|
|
||||||
echo Testing Python and service script...
|
|
||||||
"%PYTHON_EXE%" "%PYTHON_SCRIPT%" --test >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Service test passed ✅
|
|
||||||
) else (
|
|
||||||
echo ⚠️ Service test failed - continuing with installation
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check port availability
|
|
||||||
echo Testing port 8765 availability...
|
|
||||||
netstat -an | findstr :8765 >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo ⚠️ Port 8765 is in use - service may conflict
|
|
||||||
) else (
|
|
||||||
echo Port 8765 available ✅
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Install service - Multiple methods
|
|
||||||
echo [7/8] Installing Windows service with multiple fallback methods...
|
|
||||||
|
|
||||||
REM Create enhanced service wrapper
|
|
||||||
set WRAPPER_BAT=%INSTALL_DIR%\enhanced_service_wrapper.bat
|
|
||||||
|
|
||||||
echo @echo off > "%WRAPPER_BAT%"
|
|
||||||
echo REM Enhanced Windows Service Wrapper - Error 1053 Fix >> "%WRAPPER_BAT%"
|
|
||||||
echo cd /d "%INSTALL_DIR%" >> "%WRAPPER_BAT%"
|
|
||||||
echo. >> "%WRAPPER_BAT%"
|
|
||||||
echo REM Log service start attempt >> "%WRAPPER_BAT%"
|
|
||||||
echo echo %%date%% %%time%% - Service wrapper starting... ^>^> "%LOG_DIR%\service_wrapper.log" >> "%WRAPPER_BAT%"
|
|
||||||
echo. >> "%WRAPPER_BAT%"
|
|
||||||
echo REM Verify files exist >> "%WRAPPER_BAT%"
|
|
||||||
echo if not exist "%PYTHON_EXE%" ( >> "%WRAPPER_BAT%"
|
|
||||||
echo echo ERROR: Python not found at %PYTHON_EXE% ^>^> "%LOG_DIR%\service_wrapper.log" >> "%WRAPPER_BAT%"
|
|
||||||
echo exit /b 1 >> "%WRAPPER_BAT%"
|
|
||||||
echo ^) >> "%WRAPPER_BAT%"
|
|
||||||
echo. >> "%WRAPPER_BAT%"
|
|
||||||
echo if not exist "%PYTHON_SCRIPT%" ( >> "%WRAPPER_BAT%"
|
|
||||||
echo echo ERROR: Service script not found ^>^> "%LOG_DIR%\service_wrapper.log" >> "%WRAPPER_BAT%"
|
|
||||||
echo exit /b 1 >> "%WRAPPER_BAT%"
|
|
||||||
echo ^) >> "%WRAPPER_BAT%"
|
|
||||||
echo. >> "%WRAPPER_BAT%"
|
|
||||||
echo REM Start service with error handling >> "%WRAPPER_BAT%"
|
|
||||||
echo echo Starting service process... ^>^> "%LOG_DIR%\service_wrapper.log" >> "%WRAPPER_BAT%"
|
|
||||||
echo "%PYTHON_EXE%" "%PYTHON_SCRIPT%" --service >> "%WRAPPER_BAT%"
|
|
||||||
echo. >> "%WRAPPER_BAT%"
|
|
||||||
echo REM Log exit code >> "%WRAPPER_BAT%"
|
|
||||||
echo echo Service exited with code %%errorlevel%% ^>^> "%LOG_DIR%\service_wrapper.log" >> "%WRAPPER_BAT%"
|
|
||||||
|
|
||||||
REM Method 1: Windows SC Service
|
|
||||||
echo Installing as Windows SC Service...
|
|
||||||
sc create "%SERVICE_NAME%" binPath= "\"%WRAPPER_BAT%\"" DisplayName= "%SERVICE_DISPLAY_NAME%" start= auto >nul 2>&1
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Windows SC Service created ✅
|
|
||||||
|
|
||||||
REM Configure recovery
|
|
||||||
sc failure "%SERVICE_NAME%" reset= 86400 actions= restart/5000/restart/5000/restart/5000 >nul 2>&1
|
|
||||||
|
|
||||||
REM Try to start service
|
|
||||||
echo Starting SC service...
|
|
||||||
net start "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo SC Service started successfully ✅
|
|
||||||
set SERVICE_METHOD=SC_SERVICE
|
|
||||||
goto :service_installed
|
|
||||||
) else (
|
|
||||||
echo ⚠️ SC Service created but failed to start - trying Task Scheduler...
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo ❌ SC Service creation failed - trying Task Scheduler...
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Method 2: Task Scheduler Service
|
|
||||||
echo Installing as Task Scheduler Service...
|
|
||||||
|
|
||||||
REM Create task XML
|
|
||||||
set TASK_XML=%INSTALL_DIR%\%SERVICE_NAME%_task.xml
|
|
||||||
|
|
||||||
echo ^<?xml version="1.0" encoding="UTF-16"?^> > "%TASK_XML%"
|
|
||||||
echo ^<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"^> >> "%TASK_XML%"
|
|
||||||
echo ^<RegistrationInfo^> >> "%TASK_XML%"
|
|
||||||
echo ^<Description^>Quality Print Service - Automatic Label Printing^</Description^> >> "%TASK_XML%"
|
|
||||||
echo ^</RegistrationInfo^> >> "%TASK_XML%"
|
|
||||||
echo ^<Triggers^> >> "%TASK_XML%"
|
|
||||||
echo ^<BootTrigger^> >> "%TASK_XML%"
|
|
||||||
echo ^<Enabled^>true^</Enabled^> >> "%TASK_XML%"
|
|
||||||
echo ^</BootTrigger^> >> "%TASK_XML%"
|
|
||||||
echo ^</Triggers^> >> "%TASK_XML%"
|
|
||||||
echo ^<Principals^> >> "%TASK_XML%"
|
|
||||||
echo ^<Principal id="Author"^> >> "%TASK_XML%"
|
|
||||||
echo ^<UserId^>S-1-5-18^</UserId^> >> "%TASK_XML%"
|
|
||||||
echo ^<RunLevel^>HighestAvailable^</RunLevel^> >> "%TASK_XML%"
|
|
||||||
echo ^</Principal^> >> "%TASK_XML%"
|
|
||||||
echo ^</Principals^> >> "%TASK_XML%"
|
|
||||||
echo ^<Settings^> >> "%TASK_XML%"
|
|
||||||
echo ^<MultipleInstancesPolicy^>IgnoreNew^</MultipleInstancesPolicy^> >> "%TASK_XML%"
|
|
||||||
echo ^<DisallowStartIfOnBatteries^>false^</DisallowStartIfOnBatteries^> >> "%TASK_XML%"
|
|
||||||
echo ^<StopIfGoingOnBatteries^>false^</StopIfGoingOnBatteries^> >> "%TASK_XML%"
|
|
||||||
echo ^<AllowHardTerminate^>true^</AllowHardTerminate^> >> "%TASK_XML%"
|
|
||||||
echo ^<StartWhenAvailable^>true^</StartWhenAvailable^> >> "%TASK_XML%"
|
|
||||||
echo ^<RunOnlyIfNetworkAvailable^>false^</RunOnlyIfNetworkAvailable^> >> "%TASK_XML%"
|
|
||||||
echo ^<AllowStartOnDemand^>true^</AllowStartOnDemand^> >> "%TASK_XML%"
|
|
||||||
echo ^<Enabled^>true^</Enabled^> >> "%TASK_XML%"
|
|
||||||
echo ^<Hidden^>false^</Hidden^> >> "%TASK_XML%"
|
|
||||||
echo ^<RestartOnFailure^> >> "%TASK_XML%"
|
|
||||||
echo ^<Interval^>PT5M^</Interval^> >> "%TASK_XML%"
|
|
||||||
echo ^<Count^>3^</Count^> >> "%TASK_XML%"
|
|
||||||
echo ^</RestartOnFailure^> >> "%TASK_XML%"
|
|
||||||
echo ^</Settings^> >> "%TASK_XML%"
|
|
||||||
echo ^<Actions Context="Author"^> >> "%TASK_XML%"
|
|
||||||
echo ^<Exec^> >> "%TASK_XML%"
|
|
||||||
echo ^<Command^>"%PYTHON_EXE%"^</Command^> >> "%TASK_XML%"
|
|
||||||
echo ^<Arguments^>"%PYTHON_SCRIPT%" --service^</Arguments^> >> "%TASK_XML%"
|
|
||||||
echo ^<WorkingDirectory^>%INSTALL_DIR%^</WorkingDirectory^> >> "%TASK_XML%"
|
|
||||||
echo ^</Exec^> >> "%TASK_XML%"
|
|
||||||
echo ^</Actions^> >> "%TASK_XML%"
|
|
||||||
echo ^</Task^> >> "%TASK_XML%"
|
|
||||||
|
|
||||||
schtasks /create /tn "%SERVICE_NAME%" /xml "%TASK_XML%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Task Scheduler service created ✅
|
|
||||||
|
|
||||||
REM Start task
|
|
||||||
schtasks /run /tn "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo Task Scheduler service started ✅
|
|
||||||
set SERVICE_METHOD=TASK_SCHEDULER
|
|
||||||
goto :service_installed
|
|
||||||
) else (
|
|
||||||
echo ⚠️ Task created but failed to start
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo ❌ Task Scheduler creation failed
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Method 3: Startup Script Fallback
|
|
||||||
echo Installing as Startup Script...
|
|
||||||
set STARTUP_DIR=%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup
|
|
||||||
set STARTUP_SCRIPT=%STARTUP_DIR%\QualityPrintService.bat
|
|
||||||
|
|
||||||
echo @echo off > "%STARTUP_SCRIPT%"
|
|
||||||
echo REM Quality Print Service - Startup Script >> "%STARTUP_SCRIPT%"
|
|
||||||
echo cd /d "%INSTALL_DIR%" >> "%STARTUP_SCRIPT%"
|
|
||||||
echo start /min "Quality Print Service" "%PYTHON_EXE%" "%PYTHON_SCRIPT%" --service >> "%STARTUP_SCRIPT%"
|
|
||||||
|
|
||||||
if exist "%STARTUP_SCRIPT%" (
|
|
||||||
echo Startup script created ✅
|
|
||||||
|
|
||||||
REM Start immediately
|
|
||||||
start /min "" "%STARTUP_SCRIPT%"
|
|
||||||
echo Service started via startup script ✅
|
|
||||||
set SERVICE_METHOD=STARTUP_SCRIPT
|
|
||||||
goto :service_installed
|
|
||||||
)
|
|
||||||
|
|
||||||
REM If we get here, all methods failed
|
|
||||||
echo ❌ All installation methods failed
|
|
||||||
goto :installation_failed
|
|
||||||
|
|
||||||
:service_installed
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Verification
|
|
||||||
echo [8/8] Verifying installation...
|
|
||||||
|
|
||||||
echo Waiting for service to start...
|
|
||||||
timeout /t 5 >nul
|
|
||||||
|
|
||||||
REM Test service connection
|
|
||||||
echo Testing service connection...
|
|
||||||
for /L %%i in (1,1,10) do (
|
|
||||||
curl -s http://localhost:8765/health >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo Service is responding ✅
|
|
||||||
goto :success
|
|
||||||
)
|
|
||||||
|
|
||||||
powershell -Command "try { Invoke-WebRequest -Uri 'http://localhost:8765/health' -UseBasicParsing } catch { exit 1 }" >nul 2>&1
|
|
||||||
if !errorLevel! equ 0 (
|
|
||||||
echo Service is responding ✅
|
|
||||||
goto :success
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Attempt %%i failed, retrying...
|
|
||||||
timeout /t 2 >nul
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ⚠️ Service installed but not responding
|
|
||||||
goto :partial_success
|
|
||||||
|
|
||||||
:success
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo INSTALLATION SUCCESSFUL! 🎉
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo ✅ Service Method: !SERVICE_METHOD!
|
|
||||||
echo ✅ Service URL: http://localhost:8765
|
|
||||||
echo ✅ Health Check: http://localhost:8765/health
|
|
||||||
echo ✅ Service will start automatically on boot
|
|
||||||
echo.
|
|
||||||
echo 📋 NEXT STEPS:
|
|
||||||
echo 1. Install Chrome extension from 'chrome_extension' folder
|
|
||||||
echo 2. Test printing from the web application
|
|
||||||
echo 3. Configure printer settings if needed
|
|
||||||
echo.
|
|
||||||
echo 📊 Service Management:
|
|
||||||
if "!SERVICE_METHOD!"=="SC_SERVICE" (
|
|
||||||
echo - Start: net start %SERVICE_NAME%
|
|
||||||
echo - Stop: net stop %SERVICE_NAME%
|
|
||||||
echo - Status: sc query %SERVICE_NAME%
|
|
||||||
) else if "!SERVICE_METHOD!"=="TASK_SCHEDULER" (
|
|
||||||
echo - Start: schtasks /run /tn %SERVICE_NAME%
|
|
||||||
echo - Stop: schtasks /end /tn %SERVICE_NAME%
|
|
||||||
echo - Status: schtasks /query /tn %SERVICE_NAME%
|
|
||||||
) else (
|
|
||||||
echo - Start: Run startup script manually
|
|
||||||
echo - Stop: End process in Task Manager
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
echo 📁 Logs: %LOG_DIR%
|
|
||||||
echo.
|
|
||||||
goto :end
|
|
||||||
|
|
||||||
:partial_success
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo INSTALLATION COMPLETED
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo ⚠️ Service installed but verification failed
|
|
||||||
echo 📋 Manual verification steps:
|
|
||||||
echo.
|
|
||||||
echo 1. Check if service is running:
|
|
||||||
if "!SERVICE_METHOD!"=="SC_SERVICE" (
|
|
||||||
echo sc query %SERVICE_NAME%
|
|
||||||
) else if "!SERVICE_METHOD!"=="TASK_SCHEDULER" (
|
|
||||||
echo schtasks /query /tn %SERVICE_NAME%
|
|
||||||
) else (
|
|
||||||
echo Check Task Manager for python.exe process
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
echo 2. Test service manually:
|
|
||||||
echo http://localhost:8765/health
|
|
||||||
echo.
|
|
||||||
echo 3. Check logs:
|
|
||||||
echo %LOG_DIR%\service_wrapper.log
|
|
||||||
echo.
|
|
||||||
echo 4. Manual start if needed:
|
|
||||||
if "!SERVICE_METHOD!"=="SC_SERVICE" (
|
|
||||||
echo net start %SERVICE_NAME%
|
|
||||||
) else if "!SERVICE_METHOD!"=="TASK_SCHEDULER" (
|
|
||||||
echo schtasks /run /tn %SERVICE_NAME%
|
|
||||||
) else (
|
|
||||||
echo Run: %STARTUP_SCRIPT%
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
goto :end
|
|
||||||
|
|
||||||
:installation_failed
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo INSTALLATION FAILED
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo ❌ All service installation methods failed
|
|
||||||
echo.
|
|
||||||
echo 📋 Troubleshooting steps:
|
|
||||||
echo 1. Verify Administrator privileges
|
|
||||||
echo 2. Check Python installation
|
|
||||||
echo 3. Ensure port 8765 is available
|
|
||||||
echo 4. Review logs in: %LOG_DIR%
|
|
||||||
echo 5. Try manual service start
|
|
||||||
echo.
|
|
||||||
echo 🔧 Manual installation:
|
|
||||||
echo cd /d "%INSTALL_DIR%"
|
|
||||||
echo "%PYTHON_EXE%" "%PYTHON_SCRIPT%" --standalone
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:end
|
|
||||||
echo Press any key to exit...
|
|
||||||
pause >nul
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
echo ========================================
|
|
||||||
echo Quality Label Print Service Installer
|
|
||||||
echo Complete Self-Contained Version
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check for administrator privileges
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ERROR: This installer requires Administrator privileges.
|
|
||||||
echo Please right-click this file and select "Run as administrator"
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [1/6] Administrator privileges confirmed ✓
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Get current directory and set variables
|
|
||||||
set CURRENT_DIR=%~dp0
|
|
||||||
set SERVICE_NAME=QualityPrintService
|
|
||||||
set SERVICE_DISPLAY_NAME=Quality Label Print Service
|
|
||||||
set INSTALL_DIR=C:\QualityPrintService
|
|
||||||
set PYTHON_SCRIPT=%INSTALL_DIR%\print_service_complete.py
|
|
||||||
set LOG_DIR=%USERPROFILE%\PrintService\logs
|
|
||||||
|
|
||||||
echo [2/6] Checking Python installation...
|
|
||||||
|
|
||||||
REM Check if Python is available
|
|
||||||
python --version >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo WARNING: Python not found in PATH
|
|
||||||
echo.
|
|
||||||
echo Installing portable Python interpreter...
|
|
||||||
|
|
||||||
REM Download portable Python (this would need to be included in the zip)
|
|
||||||
if exist "%CURRENT_DIR%python_portable" (
|
|
||||||
echo Using included portable Python ✓
|
|
||||||
set PYTHON_EXE=%CURRENT_DIR%python_portable\python.exe
|
|
||||||
) else (
|
|
||||||
echo ERROR: Portable Python not found in package
|
|
||||||
echo Please ensure python_portable folder is included
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo Python found in system PATH ✓
|
|
||||||
set PYTHON_EXE=python
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [3/6] Creating installation directories...
|
|
||||||
|
|
||||||
REM Create installation directory
|
|
||||||
if not exist "%INSTALL_DIR%" (
|
|
||||||
mkdir "%INSTALL_DIR%"
|
|
||||||
echo Created: %INSTALL_DIR% ✓
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Create log directory
|
|
||||||
if not exist "%LOG_DIR%" (
|
|
||||||
mkdir "%LOG_DIR%"
|
|
||||||
echo Created: %LOG_DIR% ✓
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [4/6] Installing service files...
|
|
||||||
|
|
||||||
REM Copy service files
|
|
||||||
copy "%CURRENT_DIR%print_service_complete.py" "%INSTALL_DIR%\" >nul
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Copied print service script ✓
|
|
||||||
) else (
|
|
||||||
echo ERROR: Failed to copy service script
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Copy additional files if they exist
|
|
||||||
if exist "%CURRENT_DIR%requirements_complete.txt" (
|
|
||||||
copy "%CURRENT_DIR%requirements_complete.txt" "%INSTALL_DIR%\" >nul
|
|
||||||
echo Copied requirements file ✓
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Copy portable Python if included
|
|
||||||
if exist "%CURRENT_DIR%python_portable" (
|
|
||||||
echo Copying portable Python interpreter...
|
|
||||||
xcopy "%CURRENT_DIR%python_portable" "%INSTALL_DIR%\python_portable\" /E /I /H /Y >nul
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Portable Python installed ✓
|
|
||||||
set PYTHON_EXE=%INSTALL_DIR%\python_portable\python.exe
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [5/6] Installing Windows service...
|
|
||||||
|
|
||||||
REM Remove existing service if it exists
|
|
||||||
sc query "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Stopping existing service...
|
|
||||||
sc stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
timeout /t 3 >nul
|
|
||||||
echo Removing existing service...
|
|
||||||
sc delete "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
timeout /t 2 >nul
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Try to install with NSSM first (if available)
|
|
||||||
if exist "%CURRENT_DIR%nssm.exe" (
|
|
||||||
echo Installing with NSSM (Non-Sucking Service Manager)...
|
|
||||||
|
|
||||||
"%CURRENT_DIR%nssm.exe" install "%SERVICE_NAME%" "%PYTHON_EXE%" "%PYTHON_SCRIPT%"
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" DisplayName "%SERVICE_DISPLAY_NAME%"
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" Description "Quality Label Printing Service for Windows"
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" Start SERVICE_AUTO_START
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" AppStdout "%LOG_DIR%\service_output.log"
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" AppStderr "%LOG_DIR%\service_error.log"
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" AppRotateFiles 1
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" AppRotateOnline 1
|
|
||||||
"%CURRENT_DIR%nssm.exe" set "%SERVICE_NAME%" AppRotateBytes 1048576
|
|
||||||
|
|
||||||
echo Windows service installed with NSSM ✓
|
|
||||||
|
|
||||||
) else (
|
|
||||||
echo Installing with Windows SC command (Enhanced Service Wrapper)...
|
|
||||||
|
|
||||||
REM Copy service wrapper
|
|
||||||
copy "%CURRENT_DIR%service_wrapper.py" "%INSTALL_DIR%\" >nul
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo WARNING: Service wrapper not found, creating basic wrapper...
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Create enhanced service wrapper batch file
|
|
||||||
set WRAPPER_BAT=%INSTALL_DIR%\service_wrapper.bat
|
|
||||||
|
|
||||||
echo @echo off > "%WRAPPER_BAT%"
|
|
||||||
echo REM Windows Print Service Wrapper - Fixed for Error 1053 >> "%WRAPPER_BAT%"
|
|
||||||
echo cd /d "%INSTALL_DIR%" >> "%WRAPPER_BAT%"
|
|
||||||
echo. >> "%WRAPPER_BAT%"
|
|
||||||
echo REM Check if Python service wrapper exists >> "%WRAPPER_BAT%"
|
|
||||||
echo if exist "%INSTALL_DIR%\service_wrapper.py" ( >> "%WRAPPER_BAT%"
|
|
||||||
echo echo Starting service with wrapper... >> "%WRAPPER_BAT%"
|
|
||||||
echo "%PYTHON_EXE%" "%INSTALL_DIR%\service_wrapper.py" >> "%WRAPPER_BAT%"
|
|
||||||
echo ^) else ( >> "%WRAPPER_BAT%"
|
|
||||||
echo echo Starting service directly... >> "%WRAPPER_BAT%"
|
|
||||||
echo "%PYTHON_EXE%" "%PYTHON_SCRIPT%" --service >> "%WRAPPER_BAT%"
|
|
||||||
echo ^) >> "%WRAPPER_BAT%"
|
|
||||||
|
|
||||||
REM Install service using sc command with enhanced wrapper
|
|
||||||
sc create "%SERVICE_NAME%" binPath= "\"%WRAPPER_BAT%\"" DisplayName= "%SERVICE_DISPLAY_NAME%" start= auto
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Windows service installed with Enhanced SC Wrapper ✓
|
|
||||||
) else (
|
|
||||||
echo ERROR: Failed to install Windows service
|
|
||||||
echo Trying scheduled task fallback...
|
|
||||||
goto :install_scheduled_task
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Configure service recovery options
|
|
||||||
echo Configuring service recovery options...
|
|
||||||
sc failure "%SERVICE_NAME%" reset= 86400 actions= restart/5000/restart/5000/restart/5000 >nul 2>&1
|
|
||||||
|
|
||||||
goto :start_service
|
|
||||||
|
|
||||||
:install_scheduled_task
|
|
||||||
echo [5/6] Installing as scheduled task (fallback)...
|
|
||||||
|
|
||||||
REM Create scheduled task as fallback
|
|
||||||
schtasks /create /tn "%SERVICE_NAME%" /tr "\"%PYTHON_EXE%\" \"%PYTHON_SCRIPT%\"" /sc onstart /ru SYSTEM /f >nul 2>&1
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Scheduled task installed ✓
|
|
||||||
|
|
||||||
REM Start the task
|
|
||||||
schtasks /run /tn "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
echo Scheduled task started ✓
|
|
||||||
) else (
|
|
||||||
echo ERROR: Failed to install scheduled task
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
goto :install_chrome_extension
|
|
||||||
|
|
||||||
:start_service
|
|
||||||
echo [6/6] Starting service...
|
|
||||||
|
|
||||||
REM Start the service
|
|
||||||
sc start "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Service started successfully ✓
|
|
||||||
) else (
|
|
||||||
echo WARNING: Service may not have started properly
|
|
||||||
echo This is normal on first install - the service will auto-start on next boot
|
|
||||||
)
|
|
||||||
|
|
||||||
:install_chrome_extension
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo Installation Complete! ✓
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo Service Details:
|
|
||||||
echo Name: %SERVICE_NAME%
|
|
||||||
echo Location: %INSTALL_DIR%
|
|
||||||
echo Logs: %LOG_DIR%
|
|
||||||
echo URL: http://localhost:8765
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Test service connectivity
|
|
||||||
echo Testing service connectivity...
|
|
||||||
timeout /t 3 >nul
|
|
||||||
|
|
||||||
powershell -Command "try { $response = Invoke-RestMethod -Uri 'http://localhost:8765/health' -TimeoutSec 10; if ($response.status -eq 'healthy') { Write-Host 'Service is responding ✓' -ForegroundColor Green } else { Write-Host 'Service responded but may have issues' -ForegroundColor Yellow } } catch { Write-Host 'Service not yet responding (this is normal on first install)' -ForegroundColor Yellow }"
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Next Steps:
|
|
||||||
echo 1. Install Chrome Extension:
|
|
||||||
echo - Open Chrome and go to chrome://extensions/
|
|
||||||
echo - Enable 'Developer mode'
|
|
||||||
echo - Click 'Load unpacked' and select the chrome_extension folder
|
|
||||||
echo.
|
|
||||||
echo 2. Test the installation:
|
|
||||||
echo - Visit: http://localhost:8765/health
|
|
||||||
echo - Expected response: {"status": "healthy"}
|
|
||||||
echo.
|
|
||||||
echo 3. The service will automatically start with Windows
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if exist "%CURRENT_DIR%chrome_extension\" (
|
|
||||||
echo Opening Chrome extension folder...
|
|
||||||
explorer "%CURRENT_DIR%chrome_extension"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Installation completed successfully!
|
|
||||||
echo The Quality Print Service is now ready to use.
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
@@ -1,666 +0,0 @@
|
|||||||
"""
|
|
||||||
Windows Print Service for Quality Label Printing
|
|
||||||
Receives PDFs from Chrome extension and prints them page by page
|
|
||||||
|
|
||||||
Windows-compatible version with comprehensive dependency support
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import tempfile
|
|
||||||
import subprocess
|
|
||||||
import platform
|
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
||||||
from urllib.parse import urlparse, parse_qs
|
|
||||||
import urllib.request
|
|
||||||
from datetime import datetime
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import shutil
|
|
||||||
import winreg
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Windows-specific imports with fallbacks
|
|
||||||
try:
|
|
||||||
import win32print
|
|
||||||
import win32api
|
|
||||||
import win32con
|
|
||||||
import win32ui
|
|
||||||
import win32gui
|
|
||||||
WINDOWS_PRINTING_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
print("Warning: pywin32 not available. Some Windows-specific features may not work.")
|
|
||||||
WINDOWS_PRINTING_AVAILABLE = False
|
|
||||||
|
|
||||||
# PDF processing imports with fallbacks
|
|
||||||
try:
|
|
||||||
from PyPDF2 import PdfReader, PdfWriter
|
|
||||||
PYPDF2_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
from pypdf import PdfReader, PdfWriter
|
|
||||||
PYPDF2_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
print("Warning: PDF processing library not available. Install PyPDF2 or pypdf.")
|
|
||||||
PYPDF2_AVAILABLE = False
|
|
||||||
|
|
||||||
# Advanced PDF processing (optional)
|
|
||||||
try:
|
|
||||||
import fitz # PyMuPDF
|
|
||||||
PYMUPDF_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
PYMUPDF_AVAILABLE = False
|
|
||||||
|
|
||||||
# Image processing for PDF conversion
|
|
||||||
try:
|
|
||||||
from PIL import Image
|
|
||||||
from pdf2image import convert_from_path
|
|
||||||
IMAGE_PROCESSING_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
IMAGE_PROCESSING_AVAILABLE = False
|
|
||||||
|
|
||||||
# HTTP requests
|
|
||||||
try:
|
|
||||||
import requests
|
|
||||||
REQUESTS_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
REQUESTS_AVAILABLE = False
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
log_dir = os.path.join(os.path.expanduser("~"), "QualityLabelPrinting", "logs")
|
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
|
||||||
log_file = os.path.join(log_dir, "print_service.log")
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler(log_file),
|
|
||||||
logging.StreamHandler(sys.stdout)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class PrintServiceHandler(BaseHTTPRequestHandler):
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
"""Override to use our logger instead of stderr"""
|
|
||||||
logger.info(f"{self.address_string()} - {format % args}")
|
|
||||||
|
|
||||||
def do_GET(self):
|
|
||||||
"""Handle GET requests"""
|
|
||||||
try:
|
|
||||||
parsed_path = urlparse(self.path)
|
|
||||||
path = parsed_path.path
|
|
||||||
|
|
||||||
if path == '/health':
|
|
||||||
self.handle_health_check()
|
|
||||||
elif path == '/printers':
|
|
||||||
self.handle_get_printers()
|
|
||||||
elif path == '/status':
|
|
||||||
self.handle_service_status()
|
|
||||||
else:
|
|
||||||
self.send_error(404, "Endpoint not found")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error handling GET request: {e}")
|
|
||||||
self.send_error(500, str(e))
|
|
||||||
|
|
||||||
def do_POST(self):
|
|
||||||
"""Handle POST requests"""
|
|
||||||
try:
|
|
||||||
parsed_path = urlparse(self.path)
|
|
||||||
path = parsed_path.path
|
|
||||||
|
|
||||||
if path == '/print_pdf':
|
|
||||||
self.handle_print_pdf()
|
|
||||||
elif path == '/print_url':
|
|
||||||
self.handle_print_url()
|
|
||||||
else:
|
|
||||||
self.send_error(404, "Endpoint not found")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error handling POST request: {e}")
|
|
||||||
self.send_error(500, str(e))
|
|
||||||
|
|
||||||
def handle_health_check(self):
|
|
||||||
"""Health check endpoint"""
|
|
||||||
response = {
|
|
||||||
"status": "healthy",
|
|
||||||
"service": "Quality Label Print Service",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"platform": "Windows",
|
|
||||||
"timestamp": datetime.now().isoformat(),
|
|
||||||
"capabilities": ["pdf_printing", "page_by_page", "printer_selection"]
|
|
||||||
}
|
|
||||||
|
|
||||||
self.send_json_response(200, response)
|
|
||||||
|
|
||||||
def handle_get_printers(self):
|
|
||||||
"""Get available printers"""
|
|
||||||
try:
|
|
||||||
printers = self.get_available_printers()
|
|
||||||
response = {
|
|
||||||
"success": True,
|
|
||||||
"printers": printers,
|
|
||||||
"count": len(printers)
|
|
||||||
}
|
|
||||||
self.send_json_response(200, response)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error getting printers: {e}")
|
|
||||||
response = {
|
|
||||||
"success": False,
|
|
||||||
"error": str(e),
|
|
||||||
"printers": []
|
|
||||||
}
|
|
||||||
self.send_json_response(500, response)
|
|
||||||
|
|
||||||
def handle_service_status(self):
|
|
||||||
"""Service status information"""
|
|
||||||
response = {
|
|
||||||
"service_name": "Quality Label Print Service",
|
|
||||||
"running": True,
|
|
||||||
"uptime": "Available",
|
|
||||||
"last_print_job": getattr(self.server, 'last_print_time', 'Never'),
|
|
||||||
"total_jobs": getattr(self.server, 'total_jobs', 0)
|
|
||||||
}
|
|
||||||
self.send_json_response(200, response)
|
|
||||||
|
|
||||||
def handle_print_pdf(self):
|
|
||||||
"""Handle PDF printing requests"""
|
|
||||||
try:
|
|
||||||
content_length = int(self.headers['Content-Length'])
|
|
||||||
post_data = self.rfile.read(content_length)
|
|
||||||
data = json.loads(post_data.decode('utf-8'))
|
|
||||||
|
|
||||||
logger.info(f"Received print request: {data}")
|
|
||||||
|
|
||||||
# Extract request data
|
|
||||||
pdf_url = data.get('pdf_url')
|
|
||||||
printer_name = data.get('printer_name', 'default')
|
|
||||||
order_id = data.get('order_id')
|
|
||||||
prod_order = data.get('prod_order')
|
|
||||||
quantity = data.get('quantity')
|
|
||||||
|
|
||||||
if not pdf_url:
|
|
||||||
raise ValueError("PDF URL is required")
|
|
||||||
|
|
||||||
# Download PDF
|
|
||||||
logger.info(f"Downloading PDF from: {pdf_url}")
|
|
||||||
pdf_path = self.download_pdf(pdf_url)
|
|
||||||
|
|
||||||
# Print PDF page by page
|
|
||||||
logger.info(f"Printing PDF to: {printer_name}")
|
|
||||||
print_result = self.print_pdf_pages(pdf_path, printer_name)
|
|
||||||
|
|
||||||
# Update service stats
|
|
||||||
self.server.last_print_time = datetime.now().isoformat()
|
|
||||||
self.server.total_jobs = getattr(self.server, 'total_jobs', 0) + 1
|
|
||||||
|
|
||||||
# Clean up temporary file
|
|
||||||
try:
|
|
||||||
os.unlink(pdf_path)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
response = {
|
|
||||||
"success": True,
|
|
||||||
"message": f"PDF printed successfully to {printer_name}",
|
|
||||||
"order_id": order_id,
|
|
||||||
"prod_order": prod_order,
|
|
||||||
"quantity": quantity,
|
|
||||||
"pages_printed": print_result.get('pages', 0),
|
|
||||||
"printer_used": print_result.get('printer', printer_name),
|
|
||||||
"method": "windows_service_page_by_page"
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(f"Print job completed: {response}")
|
|
||||||
self.send_json_response(200, response)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error printing PDF: {e}")
|
|
||||||
response = {
|
|
||||||
"success": False,
|
|
||||||
"error": str(e),
|
|
||||||
"method": "windows_service_error"
|
|
||||||
}
|
|
||||||
self.send_json_response(500, response)
|
|
||||||
|
|
||||||
def handle_print_url(self):
|
|
||||||
"""Alternative endpoint that accepts PDF URL and prints it"""
|
|
||||||
try:
|
|
||||||
content_length = int(self.headers['Content-Length'])
|
|
||||||
post_data = self.rfile.read(content_length)
|
|
||||||
data = json.loads(post_data.decode('utf-8'))
|
|
||||||
|
|
||||||
# Same logic as handle_print_pdf
|
|
||||||
self.handle_print_pdf()
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error in print_url endpoint: {e}")
|
|
||||||
self.send_error(500, str(e))
|
|
||||||
|
|
||||||
def download_pdf(self, pdf_url):
|
|
||||||
"""Download PDF from URL to temporary file"""
|
|
||||||
try:
|
|
||||||
# Create temporary file
|
|
||||||
temp_dir = tempfile.gettempdir()
|
|
||||||
temp_file = tempfile.NamedTemporaryFile(
|
|
||||||
suffix='.pdf',
|
|
||||||
prefix='quality_label_',
|
|
||||||
delete=False,
|
|
||||||
dir=temp_dir
|
|
||||||
)
|
|
||||||
temp_path = temp_file.name
|
|
||||||
temp_file.close()
|
|
||||||
|
|
||||||
logger.info(f"Downloading PDF to: {temp_path}")
|
|
||||||
|
|
||||||
# Download the PDF
|
|
||||||
urllib.request.urlretrieve(pdf_url, temp_path)
|
|
||||||
|
|
||||||
logger.info(f"PDF downloaded successfully: {os.path.getsize(temp_path)} bytes")
|
|
||||||
return temp_path
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error downloading PDF: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def print_pdf_pages(self, pdf_path, printer_name):
|
|
||||||
"""Print PDF page by page using Windows printing"""
|
|
||||||
try:
|
|
||||||
logger.info(f"Starting page-by-page printing of: {pdf_path}")
|
|
||||||
|
|
||||||
# Method 1: Use Adobe Reader command line (if available)
|
|
||||||
adobe_result = self.try_adobe_print(pdf_path, printer_name)
|
|
||||||
if adobe_result['success']:
|
|
||||||
return adobe_result
|
|
||||||
|
|
||||||
# Method 2: Use SumatraPDF (lightweight, good for automation)
|
|
||||||
sumatra_result = self.try_sumatra_print(pdf_path, printer_name)
|
|
||||||
if sumatra_result['success']:
|
|
||||||
return sumatra_result
|
|
||||||
|
|
||||||
# Method 3: Use Windows PowerShell
|
|
||||||
powershell_result = self.try_powershell_print(pdf_path, printer_name)
|
|
||||||
if powershell_result['success']:
|
|
||||||
return powershell_result
|
|
||||||
|
|
||||||
# Method 4: Use Python PDF library + Windows printing
|
|
||||||
python_result = self.try_python_print(pdf_path, printer_name)
|
|
||||||
return python_result
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error printing PDF pages: {e}")
|
|
||||||
return {"success": False, "error": str(e), "pages": 0}
|
|
||||||
|
|
||||||
def try_adobe_print(self, pdf_path, printer_name):
|
|
||||||
"""Try printing with Adobe Reader"""
|
|
||||||
try:
|
|
||||||
# Common Adobe Reader paths
|
|
||||||
adobe_paths = [
|
|
||||||
r"C:\Program Files\Adobe\Acrobat DC\Acrobat\Acrobat.exe",
|
|
||||||
r"C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe",
|
|
||||||
r"C:\Program Files\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe"
|
|
||||||
]
|
|
||||||
|
|
||||||
adobe_path = None
|
|
||||||
for path in adobe_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
adobe_path = path
|
|
||||||
break
|
|
||||||
|
|
||||||
if not adobe_path:
|
|
||||||
return {"success": False, "reason": "Adobe Reader not found"}
|
|
||||||
|
|
||||||
# Adobe command line printing
|
|
||||||
if printer_name == 'default':
|
|
||||||
cmd = [adobe_path, "/t", pdf_path]
|
|
||||||
else:
|
|
||||||
cmd = [adobe_path, "/t", pdf_path, printer_name]
|
|
||||||
|
|
||||||
logger.info(f"Running Adobe print command: {cmd}")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
logger.info("Adobe Reader print successful")
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"method": "adobe_reader",
|
|
||||||
"printer": printer_name,
|
|
||||||
"pages": "unknown" # Adobe doesn't return page count
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
logger.warning(f"Adobe print failed: {result.stderr}")
|
|
||||||
return {"success": False, "reason": f"Adobe error: {result.stderr}"}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Adobe print method failed: {e}")
|
|
||||||
return {"success": False, "reason": str(e)}
|
|
||||||
|
|
||||||
def try_sumatra_print(self, pdf_path, printer_name):
|
|
||||||
"""Try printing with SumatraPDF"""
|
|
||||||
try:
|
|
||||||
# SumatraPDF is lightweight and good for automation
|
|
||||||
sumatra_paths = [
|
|
||||||
r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
|
|
||||||
r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe"
|
|
||||||
]
|
|
||||||
|
|
||||||
sumatra_path = None
|
|
||||||
for path in sumatra_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
sumatra_path = path
|
|
||||||
break
|
|
||||||
|
|
||||||
if not sumatra_path:
|
|
||||||
return {"success": False, "reason": "SumatraPDF not found"}
|
|
||||||
|
|
||||||
# SumatraPDF command line printing
|
|
||||||
if printer_name == 'default':
|
|
||||||
cmd = [sumatra_path, "-print-to-default", pdf_path]
|
|
||||||
else:
|
|
||||||
cmd = [sumatra_path, "-print-to", printer_name, pdf_path]
|
|
||||||
|
|
||||||
logger.info(f"Running SumatraPDF command: {cmd}")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
logger.info("SumatraPDF print successful")
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"method": "sumatra_pdf",
|
|
||||||
"printer": printer_name,
|
|
||||||
"pages": "unknown"
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
logger.warning(f"SumatraPDF print failed: {result.stderr}")
|
|
||||||
return {"success": False, "reason": f"SumatraPDF error: {result.stderr}"}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"SumatraPDF print method failed: {e}")
|
|
||||||
return {"success": False, "reason": str(e)}
|
|
||||||
|
|
||||||
def try_powershell_print(self, pdf_path, printer_name):
|
|
||||||
"""Try printing with PowerShell"""
|
|
||||||
try:
|
|
||||||
# PowerShell script to print PDF
|
|
||||||
if printer_name == 'default':
|
|
||||||
ps_script = f'''
|
|
||||||
Add-Type -AssemblyName System.Drawing
|
|
||||||
$pdf = New-Object System.Drawing.Printing.PrintDocument
|
|
||||||
$pdf.DocumentName = "{os.path.basename(pdf_path)}"
|
|
||||||
Start-Process -FilePath "{pdf_path}" -Verb Print -Wait
|
|
||||||
'''
|
|
||||||
else:
|
|
||||||
ps_script = f'''
|
|
||||||
Add-Type -AssemblyName System.Drawing
|
|
||||||
$pdf = New-Object System.Drawing.Printing.PrintDocument
|
|
||||||
$pdf.PrinterSettings.PrinterName = "{printer_name}"
|
|
||||||
$pdf.DocumentName = "{os.path.basename(pdf_path)}"
|
|
||||||
Start-Process -FilePath "{pdf_path}" -Verb Print -Wait
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Execute PowerShell
|
|
||||||
cmd = ["powershell", "-Command", ps_script]
|
|
||||||
logger.info("Running PowerShell print command")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=45)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
logger.info("PowerShell print successful")
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"method": "powershell",
|
|
||||||
"printer": printer_name,
|
|
||||||
"pages": "unknown"
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
logger.warning(f"PowerShell print failed: {result.stderr}")
|
|
||||||
return {"success": False, "reason": f"PowerShell error: {result.stderr}"}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"PowerShell print method failed: {e}")
|
|
||||||
return {"success": False, "reason": str(e)}
|
|
||||||
|
|
||||||
def try_python_print(self, pdf_path, printer_name):
|
|
||||||
"""Try printing with Python libraries"""
|
|
||||||
try:
|
|
||||||
# This would require additional libraries like win32print
|
|
||||||
# For now, return a fallback method
|
|
||||||
logger.info("Python print method - using default system print")
|
|
||||||
|
|
||||||
# Use Windows default print handler
|
|
||||||
if printer_name == 'default':
|
|
||||||
os.startfile(pdf_path, "print")
|
|
||||||
else:
|
|
||||||
# More complex implementation would be needed for specific printer
|
|
||||||
os.startfile(pdf_path, "print")
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"method": "python_system_print",
|
|
||||||
"printer": printer_name,
|
|
||||||
"pages": "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Python print method failed: {e}")
|
|
||||||
return {"success": False, "reason": str(e)}
|
|
||||||
|
|
||||||
def get_available_printers(self):
|
|
||||||
"""Get list of available printers on Windows with comprehensive detection"""
|
|
||||||
printers = []
|
|
||||||
|
|
||||||
# Method 1: Windows API (most reliable if pywin32 is available)
|
|
||||||
if WINDOWS_PRINTING_AVAILABLE:
|
|
||||||
try:
|
|
||||||
printer_info = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS)
|
|
||||||
for printer in printer_info:
|
|
||||||
printer_name = printer[2] # Printer name is at index 2
|
|
||||||
|
|
||||||
# Check if this is the default printer
|
|
||||||
try:
|
|
||||||
default_printer = win32print.GetDefaultPrinter()
|
|
||||||
is_default = (printer_name == default_printer)
|
|
||||||
except:
|
|
||||||
is_default = False
|
|
||||||
|
|
||||||
printers.append({
|
|
||||||
"name": printer_name,
|
|
||||||
"display_name": printer_name,
|
|
||||||
"is_default": is_default,
|
|
||||||
"status": "available",
|
|
||||||
"type": "Windows API"
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.info(f"Found {len(printers)} printers via Windows API")
|
|
||||||
return printers
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Windows API printer enumeration failed: {e}")
|
|
||||||
|
|
||||||
# Method 2: PowerShell (good fallback)
|
|
||||||
if not printers:
|
|
||||||
try:
|
|
||||||
cmd = ["powershell", "-Command", "Get-Printer | Select-Object Name,Default | ConvertTo-Json"]
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15, shell=True)
|
|
||||||
|
|
||||||
if result.returncode == 0 and result.stdout.strip():
|
|
||||||
try:
|
|
||||||
printers_data = json.loads(result.stdout)
|
|
||||||
if not isinstance(printers_data, list):
|
|
||||||
printers_data = [printers_data] # Single printer case
|
|
||||||
|
|
||||||
for printer in printers_data:
|
|
||||||
printers.append({
|
|
||||||
"name": printer.get("Name", "Unknown"),
|
|
||||||
"display_name": printer.get("Name", "Unknown"),
|
|
||||||
"is_default": printer.get("Default", False),
|
|
||||||
"status": "available",
|
|
||||||
"type": "PowerShell"
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.info(f"Found {len(printers)} printers via PowerShell")
|
|
||||||
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logger.warning(f"Failed to parse PowerShell printer JSON: {e}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"PowerShell printer enumeration failed: {e}")
|
|
||||||
|
|
||||||
# Method 3: WMIC command (older Windows compatibility)
|
|
||||||
if not printers:
|
|
||||||
try:
|
|
||||||
result = subprocess.run(['wmic', 'printer', 'get', 'name', '/format:csv'],
|
|
||||||
capture_output=True, text=True, shell=True, timeout=10)
|
|
||||||
if result.returncode == 0:
|
|
||||||
lines = result.stdout.strip().split('\n')[1:] # Skip header
|
|
||||||
for line in lines:
|
|
||||||
if line.strip() and ',' in line:
|
|
||||||
parts = line.split(',')
|
|
||||||
if len(parts) >= 2 and parts[1].strip():
|
|
||||||
printer_name = parts[1].strip()
|
|
||||||
printers.append({
|
|
||||||
"name": printer_name,
|
|
||||||
"display_name": printer_name,
|
|
||||||
"is_default": False,
|
|
||||||
"status": "available",
|
|
||||||
"type": "WMIC"
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.info(f"Found {len(printers)} printers via WMIC")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"WMIC printer enumeration failed: {e}")
|
|
||||||
|
|
||||||
# Method 4: Registry search (comprehensive fallback)
|
|
||||||
if not printers:
|
|
||||||
try:
|
|
||||||
import winreg
|
|
||||||
reg_path = r"SYSTEM\CurrentControlSet\Control\Print\Printers"
|
|
||||||
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path) as key:
|
|
||||||
i = 0
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
printer_name = winreg.EnumKey(key, i)
|
|
||||||
printers.append({
|
|
||||||
"name": printer_name,
|
|
||||||
"display_name": printer_name,
|
|
||||||
"is_default": False,
|
|
||||||
"status": "available",
|
|
||||||
"type": "Registry"
|
|
||||||
})
|
|
||||||
i += 1
|
|
||||||
except OSError:
|
|
||||||
break
|
|
||||||
|
|
||||||
logger.info(f"Found {len(printers)} printers via Registry")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Registry printer enumeration failed: {e}")
|
|
||||||
|
|
||||||
# Fallback: Add common Windows printers
|
|
||||||
if not printers:
|
|
||||||
common_printers = [
|
|
||||||
("Microsoft Print to PDF", True),
|
|
||||||
("Microsoft XPS Document Writer", False),
|
|
||||||
("OneNote for Windows 10", False),
|
|
||||||
("Fax", False)
|
|
||||||
]
|
|
||||||
|
|
||||||
for printer_name, is_default in common_printers:
|
|
||||||
printers.append({
|
|
||||||
"name": printer_name,
|
|
||||||
"display_name": f"{printer_name} (Built-in)",
|
|
||||||
"is_default": is_default,
|
|
||||||
"status": "available",
|
|
||||||
"type": "Built-in"
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.info(f"Using {len(printers)} built-in printer options")
|
|
||||||
|
|
||||||
# Ensure we have at least one default option
|
|
||||||
if not printers:
|
|
||||||
printers.append({
|
|
||||||
"name": "default",
|
|
||||||
"display_name": "System Default Printer",
|
|
||||||
"is_default": True,
|
|
||||||
"status": "available",
|
|
||||||
"type": "Fallback"
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.info(f"Total available printers: {len(printers)}")
|
|
||||||
return printers
|
|
||||||
|
|
||||||
def send_json_response(self, status_code, data):
|
|
||||||
"""Send JSON response"""
|
|
||||||
self.send_response(status_code)
|
|
||||||
self.send_header('Content-type', 'application/json')
|
|
||||||
self.send_header('Access-Control-Allow-Origin', '*')
|
|
||||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
|
||||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
json_data = json.dumps(data, indent=2)
|
|
||||||
self.wfile.write(json_data.encode('utf-8'))
|
|
||||||
|
|
||||||
def do_OPTIONS(self):
|
|
||||||
"""Handle CORS preflight requests"""
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header('Access-Control-Allow-Origin', '*')
|
|
||||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
|
||||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
class PrintService:
|
|
||||||
def __init__(self, port=8765):
|
|
||||||
self.port = port
|
|
||||||
self.server = None
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
"""Start the print service"""
|
|
||||||
try:
|
|
||||||
logger.info(f"Starting Quality Label Print Service on port {self.port}")
|
|
||||||
|
|
||||||
self.server = HTTPServer(('localhost', self.port), PrintServiceHandler)
|
|
||||||
self.server.total_jobs = 0
|
|
||||||
self.server.last_print_time = None
|
|
||||||
|
|
||||||
logger.info(f"Print service running at http://localhost:{self.port}")
|
|
||||||
logger.info("Available endpoints:")
|
|
||||||
logger.info(" GET /health - Health check")
|
|
||||||
logger.info(" GET /printers - List available printers")
|
|
||||||
logger.info(" GET /status - Service status")
|
|
||||||
logger.info(" POST /print_pdf - Print PDF from URL")
|
|
||||||
|
|
||||||
self.running = True
|
|
||||||
self.server.serve_forever()
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("Service stopped by user")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Service error: {e}")
|
|
||||||
finally:
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
"""Stop the print service"""
|
|
||||||
if self.server:
|
|
||||||
logger.info("Shutting down print service...")
|
|
||||||
self.server.shutdown()
|
|
||||||
self.server.server_close()
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
service = PrintService(port=8765)
|
|
||||||
try:
|
|
||||||
service.start()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\nService stopped by user")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Service failed to start: {e}")
|
|
||||||
logger.error(f"Service failed to start: {e}")
|
|
||||||
@@ -1,642 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Windows Print Service - Complete Self-Contained Version
|
|
||||||
Fixed for Windows Service Error 1053 - Proper Service Implementation
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
import time
|
|
||||||
import signal
|
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
|
||||||
import threading
|
|
||||||
import webbrowser
|
|
||||||
from urllib.parse import urlparse, unquote
|
|
||||||
import urllib.request
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
# Built-in HTTP server modules
|
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
||||||
from socketserver import ThreadingMixIn
|
|
||||||
|
|
||||||
# Global variables for service control
|
|
||||||
service_running = True
|
|
||||||
httpd_server = None
|
|
||||||
|
|
||||||
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
|
|
||||||
"""Handle requests in a separate thread."""
|
|
||||||
daemon_threads = True
|
|
||||||
allow_reuse_address = True
|
|
||||||
|
|
||||||
class PrintServiceHandler(BaseHTTPRequestHandler):
|
|
||||||
"""HTTP request handler for the print service."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.temp_dir = tempfile.mkdtemp(prefix="print_service_")
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
"""Override default logging to use our logger."""
|
|
||||||
logging.info(f"{self.client_address[0]} - {format % args}")
|
|
||||||
|
|
||||||
def do_GET(self):
|
|
||||||
"""Handle GET requests."""
|
|
||||||
try:
|
|
||||||
if self.path == '/health':
|
|
||||||
self.send_health_response()
|
|
||||||
elif self.path == '/printers':
|
|
||||||
self.send_printers_response()
|
|
||||||
elif self.path == '/status':
|
|
||||||
self.send_status_response()
|
|
||||||
else:
|
|
||||||
self.send_error(404, "Endpoint not found")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"GET request error: {e}")
|
|
||||||
self.send_error(500, str(e))
|
|
||||||
|
|
||||||
def do_POST(self):
|
|
||||||
"""Handle POST requests."""
|
|
||||||
try:
|
|
||||||
if self.path == '/print_pdf':
|
|
||||||
self.handle_print_pdf()
|
|
||||||
else:
|
|
||||||
self.send_error(404, "Endpoint not found")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"POST request error: {e}")
|
|
||||||
self.send_error(500, str(e))
|
|
||||||
|
|
||||||
def send_health_response(self):
|
|
||||||
"""Send health check response."""
|
|
||||||
response = {
|
|
||||||
"status": "healthy",
|
|
||||||
"service": "Windows Print Service",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"timestamp": datetime.now().isoformat(),
|
|
||||||
"platform": sys.platform,
|
|
||||||
"python_version": sys.version,
|
|
||||||
"temp_dir": self.temp_dir
|
|
||||||
}
|
|
||||||
self.send_json_response(response)
|
|
||||||
|
|
||||||
def send_printers_response(self):
|
|
||||||
"""Send available printers list."""
|
|
||||||
printers = self.get_available_printers()
|
|
||||||
response = {
|
|
||||||
"success": True,
|
|
||||||
"printers": printers,
|
|
||||||
"count": len(printers),
|
|
||||||
"timestamp": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
self.send_json_response(response)
|
|
||||||
|
|
||||||
def send_status_response(self):
|
|
||||||
"""Send service status."""
|
|
||||||
response = {
|
|
||||||
"service_name": "Windows Print Service",
|
|
||||||
"status": "running",
|
|
||||||
"uptime": time.time() - start_time,
|
|
||||||
"requests_handled": getattr(self.server, 'request_count', 0),
|
|
||||||
"last_activity": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
self.send_json_response(response)
|
|
||||||
|
|
||||||
def handle_print_pdf(self):
|
|
||||||
"""Handle PDF printing request."""
|
|
||||||
try:
|
|
||||||
# Get content length
|
|
||||||
content_length = int(self.headers['Content-Length'])
|
|
||||||
post_data = self.rfile.read(content_length).decode('utf-8')
|
|
||||||
|
|
||||||
# Parse JSON data
|
|
||||||
try:
|
|
||||||
data = json.loads(post_data)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
self.send_error(400, "Invalid JSON data")
|
|
||||||
return
|
|
||||||
|
|
||||||
pdf_url = data.get('pdf_url')
|
|
||||||
printer_name = data.get('printer_name', 'default')
|
|
||||||
|
|
||||||
if not pdf_url:
|
|
||||||
self.send_error(400, "Missing pdf_url parameter")
|
|
||||||
return
|
|
||||||
|
|
||||||
logging.info(f"Print request - URL: {pdf_url}, Printer: {printer_name}")
|
|
||||||
|
|
||||||
# Download and print PDF
|
|
||||||
result = self.download_and_print_pdf(pdf_url, printer_name)
|
|
||||||
|
|
||||||
if result['success']:
|
|
||||||
response = {
|
|
||||||
"success": True,
|
|
||||||
"message": "PDF sent to printer successfully",
|
|
||||||
"printer": printer_name,
|
|
||||||
"method": result.get('method', 'unknown'),
|
|
||||||
"timestamp": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
response = {
|
|
||||||
"success": False,
|
|
||||||
"error": result.get('error', 'Unknown error'),
|
|
||||||
"timestamp": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.send_json_response(response)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Print PDF error: {e}")
|
|
||||||
response = {
|
|
||||||
"success": False,
|
|
||||||
"error": str(e),
|
|
||||||
"timestamp": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
self.send_json_response(response)
|
|
||||||
|
|
||||||
def download_and_print_pdf(self, pdf_url, printer_name):
|
|
||||||
"""Download PDF and send to printer."""
|
|
||||||
try:
|
|
||||||
# Create unique filename
|
|
||||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
||||||
pdf_filename = f"print_job_{timestamp}.pdf"
|
|
||||||
pdf_path = os.path.join(self.temp_dir, pdf_filename)
|
|
||||||
|
|
||||||
# Download PDF
|
|
||||||
logging.info(f"Downloading PDF from: {pdf_url}")
|
|
||||||
urllib.request.urlretrieve(pdf_url, pdf_path)
|
|
||||||
|
|
||||||
if not os.path.exists(pdf_path):
|
|
||||||
return {"success": False, "error": "Failed to download PDF"}
|
|
||||||
|
|
||||||
logging.info(f"PDF downloaded to: {pdf_path}")
|
|
||||||
|
|
||||||
# Try different printing methods
|
|
||||||
print_methods = [
|
|
||||||
self.print_with_adobe_reader,
|
|
||||||
self.print_with_sumatra_pdf,
|
|
||||||
self.print_with_powershell,
|
|
||||||
self.print_with_edge,
|
|
||||||
self.print_with_system_default
|
|
||||||
]
|
|
||||||
|
|
||||||
for method in print_methods:
|
|
||||||
try:
|
|
||||||
result = method(pdf_path, printer_name)
|
|
||||||
if result['success']:
|
|
||||||
# Clean up downloaded file after successful print
|
|
||||||
try:
|
|
||||||
os.remove(pdf_path)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return result
|
|
||||||
except Exception as e:
|
|
||||||
logging.warning(f"Print method {method.__name__} failed: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
return {"success": False, "error": "All printing methods failed"}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Download and print error: {e}")
|
|
||||||
return {"success": False, "error": str(e)}
|
|
||||||
|
|
||||||
def print_with_adobe_reader(self, pdf_path, printer_name):
|
|
||||||
"""Print using Adobe Reader command line."""
|
|
||||||
try:
|
|
||||||
# Common Adobe Reader paths
|
|
||||||
adobe_paths = [
|
|
||||||
r"C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe",
|
|
||||||
r"C:\Program Files\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe",
|
|
||||||
r"C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe",
|
|
||||||
r"C:\Program Files\Adobe\Reader 11.0\Reader\AcroRd32.exe"
|
|
||||||
]
|
|
||||||
|
|
||||||
adobe_exe = None
|
|
||||||
for path in adobe_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
adobe_exe = path
|
|
||||||
break
|
|
||||||
|
|
||||||
if not adobe_exe:
|
|
||||||
return {"success": False, "error": "Adobe Reader not found"}
|
|
||||||
|
|
||||||
# Build command
|
|
||||||
if printer_name == 'default':
|
|
||||||
cmd = [adobe_exe, "/t", pdf_path]
|
|
||||||
else:
|
|
||||||
cmd = [adobe_exe, "/t", pdf_path, printer_name]
|
|
||||||
|
|
||||||
logging.info(f"Adobe Reader command: {' '.join(cmd)}")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
return {"success": True, "method": "Adobe Reader"}
|
|
||||||
else:
|
|
||||||
return {"success": False, "error": f"Adobe Reader failed: {result.stderr}"}
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
return {"success": False, "error": "Adobe Reader timeout"}
|
|
||||||
except Exception as e:
|
|
||||||
return {"success": False, "error": f"Adobe Reader error: {e}"}
|
|
||||||
|
|
||||||
def print_with_sumatra_pdf(self, pdf_path, printer_name):
|
|
||||||
"""Print using SumatraPDF."""
|
|
||||||
try:
|
|
||||||
# Common SumatraPDF paths
|
|
||||||
sumatra_paths = [
|
|
||||||
r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
|
|
||||||
r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
|
|
||||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'SumatraPDF', 'SumatraPDF.exe')
|
|
||||||
]
|
|
||||||
|
|
||||||
sumatra_exe = None
|
|
||||||
for path in sumatra_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
sumatra_exe = path
|
|
||||||
break
|
|
||||||
|
|
||||||
if not sumatra_exe:
|
|
||||||
return {"success": False, "error": "SumatraPDF not found"}
|
|
||||||
|
|
||||||
# Build command
|
|
||||||
if printer_name == 'default':
|
|
||||||
cmd = [sumatra_exe, "-print-dialog", pdf_path]
|
|
||||||
else:
|
|
||||||
cmd = [sumatra_exe, "-print-to", printer_name, pdf_path]
|
|
||||||
|
|
||||||
logging.info(f"SumatraPDF command: {' '.join(cmd)}")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
|
|
||||||
return {"success": True, "method": "SumatraPDF"}
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
return {"success": False, "error": "SumatraPDF timeout"}
|
|
||||||
except Exception as e:
|
|
||||||
return {"success": False, "error": f"SumatraPDF error: {e}"}
|
|
||||||
|
|
||||||
def print_with_powershell(self, pdf_path, printer_name):
|
|
||||||
"""Print using PowerShell."""
|
|
||||||
try:
|
|
||||||
if printer_name == 'default':
|
|
||||||
powershell_cmd = f'''
|
|
||||||
$pdf = "{pdf_path}"
|
|
||||||
Start-Process -FilePath $pdf -Verb Print -WindowStyle Hidden
|
|
||||||
'''
|
|
||||||
else:
|
|
||||||
# Use specific printer with PowerShell
|
|
||||||
powershell_cmd = f'''
|
|
||||||
$printer = "{printer_name}"
|
|
||||||
$pdf = "{pdf_path}"
|
|
||||||
$shell = New-Object -ComObject Shell.Application
|
|
||||||
$file = $shell.NameSpace((Get-Item $pdf).DirectoryName).ParseName((Get-Item $pdf).Name)
|
|
||||||
$file.InvokeVerb("print")
|
|
||||||
'''
|
|
||||||
|
|
||||||
cmd = ["powershell", "-Command", powershell_cmd]
|
|
||||||
logging.info("PowerShell print command executed")
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
return {"success": True, "method": "PowerShell"}
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
return {"success": False, "error": "PowerShell timeout"}
|
|
||||||
except Exception as e:
|
|
||||||
return {"success": False, "error": f"PowerShell error: {e}"}
|
|
||||||
|
|
||||||
def print_with_edge(self, pdf_path, printer_name):
|
|
||||||
"""Print using Microsoft Edge."""
|
|
||||||
try:
|
|
||||||
# Convert to file URL
|
|
||||||
file_url = f"file:///{pdf_path.replace(os.sep, '/')}"
|
|
||||||
|
|
||||||
cmd = ["msedge", "--print-to-pdf", "--run-all-compositor-stages-before-draw", file_url]
|
|
||||||
logging.info("Edge print command executed")
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
return {"success": True, "method": "Microsoft Edge"}
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
return {"success": False, "error": "Edge timeout"}
|
|
||||||
except Exception as e:
|
|
||||||
return {"success": False, "error": f"Edge error: {e}"}
|
|
||||||
|
|
||||||
def print_with_system_default(self, pdf_path, printer_name):
|
|
||||||
"""Print using system default application."""
|
|
||||||
try:
|
|
||||||
# Use Windows shell to open and print
|
|
||||||
cmd = ["cmd", "/c", "start", "/wait", pdf_path]
|
|
||||||
logging.info("System default print executed")
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
||||||
return {"success": True, "method": "System Default"}
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
return {"success": False, "error": "System default timeout"}
|
|
||||||
except Exception as e:
|
|
||||||
return {"success": False, "error": f"System default error: {e}"}
|
|
||||||
|
|
||||||
def get_available_printers(self):
|
|
||||||
"""Get list of available printers using Windows commands."""
|
|
||||||
try:
|
|
||||||
printers = []
|
|
||||||
|
|
||||||
# Method 1: Use PowerShell to get printers
|
|
||||||
try:
|
|
||||||
cmd = ["powershell", "-Command", "Get-Printer | Select-Object Name, Type, PrinterStatus | ConvertTo-Json"]
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
printer_data = json.loads(result.stdout)
|
|
||||||
if isinstance(printer_data, list):
|
|
||||||
for printer in printer_data:
|
|
||||||
printers.append({
|
|
||||||
"name": printer.get("Name", "Unknown"),
|
|
||||||
"type": printer.get("Type", "Unknown"),
|
|
||||||
"status": printer.get("PrinterStatus", "Unknown"),
|
|
||||||
"is_default": False
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
printers.append({
|
|
||||||
"name": printer_data.get("Name", "Unknown"),
|
|
||||||
"type": printer_data.get("Type", "Unknown"),
|
|
||||||
"status": printer_data.get("PrinterStatus", "Unknown"),
|
|
||||||
"is_default": False
|
|
||||||
})
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Method 2: Use wmic command as fallback
|
|
||||||
if not printers:
|
|
||||||
try:
|
|
||||||
cmd = ["wmic", "printer", "get", "name,default", "/format:csv"]
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
lines = result.stdout.strip().split('\n')[1:] # Skip header
|
|
||||||
for line in lines:
|
|
||||||
parts = line.split(',')
|
|
||||||
if len(parts) >= 3:
|
|
||||||
printer_name = parts[2].strip()
|
|
||||||
is_default = parts[1].strip().lower() == 'true'
|
|
||||||
if printer_name:
|
|
||||||
printers.append({
|
|
||||||
"name": printer_name,
|
|
||||||
"type": "Windows Printer",
|
|
||||||
"status": "Available",
|
|
||||||
"is_default": is_default
|
|
||||||
})
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Add default fallback printers
|
|
||||||
if not printers:
|
|
||||||
printers = [
|
|
||||||
{"name": "Microsoft Print to PDF", "type": "Virtual", "status": "Available", "is_default": False},
|
|
||||||
{"name": "Default Printer", "type": "System", "status": "Available", "is_default": True}
|
|
||||||
]
|
|
||||||
|
|
||||||
return printers
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error getting printers: {e}")
|
|
||||||
return [{"name": "Default Printer", "type": "System", "status": "Unknown", "is_default": True}]
|
|
||||||
|
|
||||||
def send_json_response(self, data, status_code=200):
|
|
||||||
"""Send JSON response."""
|
|
||||||
self.send_response(status_code)
|
|
||||||
self.send_header('Content-type', 'application/json')
|
|
||||||
self.send_header('Access-Control-Allow-Origin', '*')
|
|
||||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
|
||||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
json_response = json.dumps(data, indent=2)
|
|
||||||
self.wfile.write(json_response.encode('utf-8'))
|
|
||||||
|
|
||||||
def setup_logging():
|
|
||||||
"""Setup logging configuration."""
|
|
||||||
log_dir = os.path.join(os.path.expanduser("~"), "PrintService", "logs")
|
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
|
||||||
|
|
||||||
log_file = os.path.join(log_dir, f"print_service_{datetime.now().strftime('%Y%m%d')}.log")
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler(log_file),
|
|
||||||
logging.StreamHandler(sys.stdout)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def create_windows_service():
|
|
||||||
"""Create Windows service registration script."""
|
|
||||||
service_script = '''
|
|
||||||
@echo off
|
|
||||||
echo Installing Windows Print Service...
|
|
||||||
|
|
||||||
REM Check for administrator privileges
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% == 0 (
|
|
||||||
echo Administrator privileges confirmed.
|
|
||||||
) else (
|
|
||||||
echo This script requires administrator privileges.
|
|
||||||
echo Please right-click and "Run as administrator"
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Get current directory
|
|
||||||
set CURRENT_DIR=%~dp0
|
|
||||||
|
|
||||||
REM Install service using sc command
|
|
||||||
sc create "WindowsPrintService" binPath= "%CURRENT_DIR%print_service_complete.exe" DisplayName= "Windows Print Service" start= auto
|
|
||||||
|
|
||||||
REM Configure service recovery options
|
|
||||||
sc failure "WindowsPrintService" reset= 86400 actions= restart/5000/restart/5000/restart/5000
|
|
||||||
|
|
||||||
REM Start the service
|
|
||||||
sc start "WindowsPrintService"
|
|
||||||
|
|
||||||
echo Windows Print Service installed and started successfully!
|
|
||||||
echo Service will auto-start with Windows.
|
|
||||||
echo.
|
|
||||||
echo Test the service: http://localhost:8765/health
|
|
||||||
pause
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('install_service_complete.bat', 'w') as f:
|
|
||||||
f.write(service_script)
|
|
||||||
|
|
||||||
def signal_handler(signum, frame):
|
|
||||||
"""Handle shutdown signals gracefully."""
|
|
||||||
global service_running, httpd_server
|
|
||||||
logging.info(f"Received signal {signum}, shutting down gracefully...")
|
|
||||||
service_running = False
|
|
||||||
|
|
||||||
if httpd_server:
|
|
||||||
# Shutdown server in a separate thread to avoid blocking
|
|
||||||
def shutdown_server():
|
|
||||||
try:
|
|
||||||
httpd_server.shutdown()
|
|
||||||
httpd_server.server_close()
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error during server shutdown: {e}")
|
|
||||||
|
|
||||||
shutdown_thread = threading.Thread(target=shutdown_server)
|
|
||||||
shutdown_thread.daemon = True
|
|
||||||
shutdown_thread.start()
|
|
||||||
shutdown_thread.join(timeout=5)
|
|
||||||
|
|
||||||
logging.info("Service shutdown complete")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def setup_signal_handlers():
|
|
||||||
"""Setup signal handlers for graceful shutdown."""
|
|
||||||
if hasattr(signal, 'SIGTERM'):
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
|
||||||
if hasattr(signal, 'SIGINT'):
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
if hasattr(signal, 'SIGBREAK'): # Windows specific
|
|
||||||
signal.signal(signal.SIGBREAK, signal_handler)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main service function with proper Windows service support."""
|
|
||||||
global start_time, service_running, httpd_server
|
|
||||||
start_time = time.time()
|
|
||||||
|
|
||||||
# Setup logging first
|
|
||||||
setup_logging()
|
|
||||||
logging.info("=== Starting Windows Print Service (Complete Version) ===")
|
|
||||||
logging.info(f"Python version: {sys.version}")
|
|
||||||
logging.info(f"Platform: {sys.platform}")
|
|
||||||
logging.info(f"Process ID: {os.getpid()}")
|
|
||||||
logging.info(f"Command line args: {sys.argv}")
|
|
||||||
|
|
||||||
# Setup signal handlers for graceful shutdown
|
|
||||||
setup_signal_handlers()
|
|
||||||
|
|
||||||
# Determine run mode
|
|
||||||
run_mode = "standalone"
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
if sys.argv[1] in ['--service', 'service']:
|
|
||||||
run_mode = "windows_service"
|
|
||||||
elif sys.argv[1] in ['--standalone', 'standalone']:
|
|
||||||
run_mode = "standalone"
|
|
||||||
elif sys.argv[1] in ['--test', 'test']:
|
|
||||||
run_mode = "test"
|
|
||||||
|
|
||||||
logging.info(f"Running in '{run_mode}' mode")
|
|
||||||
|
|
||||||
if run_mode == "test":
|
|
||||||
# Test mode - just verify setup and exit
|
|
||||||
logging.info("=== SERVICE TEST MODE ===")
|
|
||||||
test_service_setup()
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Start HTTP server
|
|
||||||
server_address = ('localhost', 8765)
|
|
||||||
|
|
||||||
# Try to bind to the port
|
|
||||||
try:
|
|
||||||
httpd_server = ThreadingHTTPServer(server_address, PrintServiceHandler)
|
|
||||||
except OSError as e:
|
|
||||||
if "Address already in use" in str(e):
|
|
||||||
logging.error(f"Port 8765 is already in use. Another service instance may be running.")
|
|
||||||
logging.error("Stop the existing service or use a different port.")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
logging.info(f"Print service started on http://{server_address[0]}:{server_address[1]}")
|
|
||||||
logging.info("Available endpoints:")
|
|
||||||
logging.info(" GET /health - Health check")
|
|
||||||
logging.info(" GET /printers - List available printers")
|
|
||||||
logging.info(" GET /status - Service status")
|
|
||||||
logging.info(" POST /print_pdf - Print PDF file")
|
|
||||||
|
|
||||||
# Keep track of requests
|
|
||||||
httpd_server.request_count = 0
|
|
||||||
|
|
||||||
# Service main loop
|
|
||||||
logging.info("Service is ready and listening...")
|
|
||||||
|
|
||||||
if run_mode == "standalone":
|
|
||||||
logging.info("*** STANDALONE MODE - Press Ctrl+C to stop ***")
|
|
||||||
logging.info("Test the service at: http://localhost:8765/health")
|
|
||||||
|
|
||||||
while service_running:
|
|
||||||
try:
|
|
||||||
# Handle requests with timeout to check service_running periodically
|
|
||||||
httpd_server.timeout = 1.0
|
|
||||||
httpd_server.handle_request()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logging.info("Service stopped by user (Ctrl+C)")
|
|
||||||
break
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Request handling error: {e}")
|
|
||||||
# Continue running unless it's a critical error
|
|
||||||
if not service_running:
|
|
||||||
break
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Critical service error: {e}")
|
|
||||||
import traceback
|
|
||||||
logging.error(f"Traceback: {traceback.format_exc()}")
|
|
||||||
finally:
|
|
||||||
# Cleanup
|
|
||||||
if httpd_server:
|
|
||||||
try:
|
|
||||||
httpd_server.server_close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
logging.info("=== Windows Print Service shutdown complete ===")
|
|
||||||
|
|
||||||
# Exit cleanly
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def test_service_setup():
|
|
||||||
"""Test service setup and configuration."""
|
|
||||||
logging.info("Testing service setup...")
|
|
||||||
|
|
||||||
# Test printer detection
|
|
||||||
try:
|
|
||||||
# Create a temporary handler instance to test printer detection
|
|
||||||
import tempfile
|
|
||||||
temp_handler = PrintServiceHandler()
|
|
||||||
temp_handler.temp_dir = tempfile.mkdtemp(prefix="test_service_")
|
|
||||||
|
|
||||||
printers = temp_handler.get_available_printers()
|
|
||||||
logging.info(f"Found {len(printers)} printers:")
|
|
||||||
for printer in printers[:5]: # Show first 5
|
|
||||||
logging.info(f" - {printer.get('name', 'Unknown')}")
|
|
||||||
|
|
||||||
# Cleanup temp directory
|
|
||||||
try:
|
|
||||||
import shutil
|
|
||||||
shutil.rmtree(temp_handler.temp_dir)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.warning(f"Printer detection test failed: {e}")
|
|
||||||
|
|
||||||
# Test port availability
|
|
||||||
try:
|
|
||||||
import socket
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
||||||
s.bind(('localhost', 8765))
|
|
||||||
logging.info("Port 8765 is available ✓")
|
|
||||||
except OSError as e:
|
|
||||||
logging.error(f"Port 8765 test failed: {e}")
|
|
||||||
|
|
||||||
logging.info("Service setup test completed")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Windows Print Service - Complete Requirements
|
|
||||||
# All dependencies needed for the self-contained service
|
|
||||||
|
|
||||||
# Core Python libraries (usually built-in)
|
|
||||||
# These should be available in any Python 3.7+ installation
|
|
||||||
|
|
||||||
# For HTTP server
|
|
||||||
http.server
|
|
||||||
socketserver
|
|
||||||
urllib.request
|
|
||||||
urllib.parse
|
|
||||||
|
|
||||||
# For system operations
|
|
||||||
os
|
|
||||||
sys
|
|
||||||
subprocess
|
|
||||||
tempfile
|
|
||||||
shutil
|
|
||||||
pathlib
|
|
||||||
|
|
||||||
# For data handling
|
|
||||||
json
|
|
||||||
logging
|
|
||||||
threading
|
|
||||||
time
|
|
||||||
datetime
|
|
||||||
|
|
||||||
# For Windows-specific operations
|
|
||||||
# These are handled by subprocess calls to Windows commands
|
|
||||||
|
|
||||||
# Optional: PyInstaller for creating standalone executable
|
|
||||||
# PyInstaller==5.13.2
|
|
||||||
|
|
||||||
# Note: This service is designed to work with Python standard library only
|
|
||||||
# No external dependencies required for basic functionality
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
"""
|
|
||||||
Windows Service Implementation - Alternative Approach
|
|
||||||
Uses Windows Task Scheduler as fallback if SC service fails
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def create_scheduled_task_service():
|
|
||||||
"""Create Windows service using Task Scheduler as fallback."""
|
|
||||||
|
|
||||||
service_name = "QualityPrintService"
|
|
||||||
service_script = Path(__file__).parent / "print_service_complete.py"
|
|
||||||
python_exe = sys.executable
|
|
||||||
|
|
||||||
# Create XML for scheduled task
|
|
||||||
task_xml = f'''<?xml version="1.0" encoding="UTF-16"?>
|
|
||||||
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
||||||
<RegistrationInfo>
|
|
||||||
<Date>2025-09-26T12:00:00</Date>
|
|
||||||
<Author>Quality Print Service</Author>
|
|
||||||
<Description>Quality Print Service - Automatic Label Printing</Description>
|
|
||||||
</RegistrationInfo>
|
|
||||||
<Triggers>
|
|
||||||
<BootTrigger>
|
|
||||||
<Enabled>true</Enabled>
|
|
||||||
</BootTrigger>
|
|
||||||
</Triggers>
|
|
||||||
<Principals>
|
|
||||||
<Principal id="Author">
|
|
||||||
<UserId>S-1-5-18</UserId>
|
|
||||||
<RunLevel>HighestAvailable</RunLevel>
|
|
||||||
</Principal>
|
|
||||||
</Principals>
|
|
||||||
<Settings>
|
|
||||||
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
||||||
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
||||||
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
||||||
<AllowHardTerminate>true</AllowHardTerminate>
|
|
||||||
<StartWhenAvailable>true</StartWhenAvailable>
|
|
||||||
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
||||||
<IdleSettings>
|
|
||||||
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
||||||
<RestartOnIdle>false</RestartOnIdle>
|
|
||||||
</IdleSettings>
|
|
||||||
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
||||||
<Enabled>true</Enabled>
|
|
||||||
<Hidden>false</Hidden>
|
|
||||||
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
||||||
<WakeToRun>false</WakeToRun>
|
|
||||||
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
||||||
<Priority>7</Priority>
|
|
||||||
<RestartOnFailure>
|
|
||||||
<Interval>PT5M</Interval>
|
|
||||||
<Count>3</Count>
|
|
||||||
</RestartOnFailure>
|
|
||||||
</Settings>
|
|
||||||
<Actions Context="Author">
|
|
||||||
<Exec>
|
|
||||||
<Command>"{python_exe}"</Command>
|
|
||||||
<Arguments>"{service_script}" --service</Arguments>
|
|
||||||
<WorkingDirectory>{service_script.parent}</WorkingDirectory>
|
|
||||||
</Exec>
|
|
||||||
</Actions>
|
|
||||||
</Task>'''
|
|
||||||
|
|
||||||
# Save task XML to file
|
|
||||||
task_xml_file = Path(__file__).parent / f"{service_name}_task.xml"
|
|
||||||
with open(task_xml_file, 'w', encoding='utf-16') as f:
|
|
||||||
f.write(task_xml)
|
|
||||||
|
|
||||||
return task_xml_file
|
|
||||||
|
|
||||||
def install_service_alternative():
|
|
||||||
"""Install service using multiple methods."""
|
|
||||||
|
|
||||||
print("🔧 Installing Windows Print Service with Multiple Methods...")
|
|
||||||
|
|
||||||
service_name = "QualityPrintService"
|
|
||||||
service_display_name = "Quality Print Service"
|
|
||||||
service_script = Path(__file__).parent / "print_service_complete.py"
|
|
||||||
python_exe = sys.executable
|
|
||||||
|
|
||||||
# Method 1: Try SC command with service wrapper
|
|
||||||
print("\n📋 Method 1: Windows SC Service")
|
|
||||||
try:
|
|
||||||
# Create service wrapper batch file
|
|
||||||
wrapper_bat = Path(__file__).parent / "service_wrapper.bat"
|
|
||||||
|
|
||||||
wrapper_content = f'''@echo off
|
|
||||||
REM Windows Print Service Wrapper - Error 1053 Fix
|
|
||||||
cd /d "{service_script.parent}"
|
|
||||||
|
|
||||||
REM Set error handling
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
REM Log startup
|
|
||||||
echo %date% %time% - Service starting... >> service_startup.log
|
|
||||||
|
|
||||||
REM Check if Python exists
|
|
||||||
if not exist "{python_exe}" (
|
|
||||||
echo ERROR: Python not found at {python_exe} >> service_startup.log
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check if service script exists
|
|
||||||
if not exist "{service_script}" (
|
|
||||||
echo ERROR: Service script not found at {service_script} >> service_startup.log
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Start the service with timeout monitoring
|
|
||||||
echo Starting service process... >> service_startup.log
|
|
||||||
"{python_exe}" "{service_script}" --service
|
|
||||||
|
|
||||||
REM Log exit
|
|
||||||
echo %date% %time% - Service exited with code %errorlevel% >> service_startup.log
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open(wrapper_bat, 'w') as f:
|
|
||||||
f.write(wrapper_content)
|
|
||||||
|
|
||||||
# Remove existing service
|
|
||||||
subprocess.run(['sc', 'stop', service_name], capture_output=True)
|
|
||||||
subprocess.run(['sc', 'delete', service_name], capture_output=True)
|
|
||||||
|
|
||||||
# Create service
|
|
||||||
cmd = [
|
|
||||||
'sc', 'create', service_name,
|
|
||||||
f'binPath= "{wrapper_bat}"',
|
|
||||||
f'DisplayName= "{service_display_name}"',
|
|
||||||
'start= auto'
|
|
||||||
]
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ SC Service created successfully")
|
|
||||||
|
|
||||||
# Configure recovery
|
|
||||||
subprocess.run([
|
|
||||||
'sc', 'failure', service_name,
|
|
||||||
'reset= 86400',
|
|
||||||
'actions= restart/5000/restart/5000/restart/5000'
|
|
||||||
], capture_output=True)
|
|
||||||
|
|
||||||
# Try to start
|
|
||||||
start_result = subprocess.run(['sc', 'start', service_name], capture_output=True, text=True)
|
|
||||||
if start_result.returncode == 0:
|
|
||||||
print("✅ SC Service started successfully")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"⚠️ SC Service created but failed to start: {start_result.stderr}")
|
|
||||||
else:
|
|
||||||
print(f"❌ SC Service creation failed: {result.stderr}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ SC Service method failed: {e}")
|
|
||||||
|
|
||||||
# Method 2: Task Scheduler fallback
|
|
||||||
print("\n📋 Method 2: Task Scheduler Service")
|
|
||||||
try:
|
|
||||||
task_xml_file = create_scheduled_task_service()
|
|
||||||
|
|
||||||
# Remove existing task
|
|
||||||
subprocess.run(['schtasks', '/delete', '/tn', service_name, '/f'], capture_output=True)
|
|
||||||
|
|
||||||
# Create task
|
|
||||||
cmd = [
|
|
||||||
'schtasks', '/create', '/tn', service_name,
|
|
||||||
'/xml', str(task_xml_file)
|
|
||||||
]
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ Task Scheduler service created successfully")
|
|
||||||
|
|
||||||
# Start task
|
|
||||||
start_result = subprocess.run(['schtasks', '/run', '/tn', service_name], capture_output=True, text=True)
|
|
||||||
if start_result.returncode == 0:
|
|
||||||
print("✅ Task Scheduler service started successfully")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Task created but failed to start: {start_result.stderr}")
|
|
||||||
else:
|
|
||||||
print(f"❌ Task Scheduler creation failed: {result.stderr}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Task Scheduler method failed: {e}")
|
|
||||||
|
|
||||||
# Method 3: Manual startup script
|
|
||||||
print("\n📋 Method 3: Startup Script")
|
|
||||||
try:
|
|
||||||
startup_script = Path(os.environ.get('APPDATA', '')) / 'Microsoft' / 'Windows' / 'Start Menu' / 'Programs' / 'Startup' / 'QualityPrintService.bat'
|
|
||||||
startup_script.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
startup_content = f'''@echo off
|
|
||||||
REM Quality Print Service - Startup Script
|
|
||||||
cd /d "{service_script.parent}"
|
|
||||||
start /min "Quality Print Service" "{python_exe}" "{service_script}" --service
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open(startup_script, 'w') as f:
|
|
||||||
f.write(startup_content)
|
|
||||||
|
|
||||||
print(f"✅ Startup script created: {startup_script}")
|
|
||||||
print("🔄 Service will start automatically on next reboot")
|
|
||||||
|
|
||||||
# Start immediately
|
|
||||||
subprocess.run(['cmd', '/c', str(startup_script)], capture_output=True)
|
|
||||||
print("✅ Service started via startup script")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Startup script method failed: {e}")
|
|
||||||
|
|
||||||
print("\n❌ All installation methods failed")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_service_connection(timeout=10):
|
|
||||||
"""Test if service is running by checking HTTP endpoint."""
|
|
||||||
import urllib.request
|
|
||||||
import json
|
|
||||||
|
|
||||||
for i in range(timeout):
|
|
||||||
try:
|
|
||||||
with urllib.request.urlopen('http://localhost:8765/health', timeout=2) as response:
|
|
||||||
data = json.loads(response.read().decode())
|
|
||||||
if data.get('status') == 'healthy':
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'install':
|
|
||||||
success = install_service_alternative()
|
|
||||||
|
|
||||||
if success:
|
|
||||||
print("\n🧪 Testing service connection...")
|
|
||||||
if test_service_connection():
|
|
||||||
print("✅ Service is responding correctly!")
|
|
||||||
print("🌐 Test URL: http://localhost:8765/health")
|
|
||||||
else:
|
|
||||||
print("⚠️ Service installed but not responding")
|
|
||||||
print("📋 Check logs and try manual start")
|
|
||||||
else:
|
|
||||||
print("\n❌ Service installation failed")
|
|
||||||
print("📋 Try running as Administrator")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
print("Usage: python service_installer.py install")
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Windows Service Wrapper - Handles Windows Service Communication
|
|
||||||
Fixes Error 1053 by properly communicating with Service Control Manager
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
import subprocess
|
|
||||||
import threading
|
|
||||||
import signal
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Simple service state management
|
|
||||||
class WindowsServiceManager:
|
|
||||||
def __init__(self):
|
|
||||||
self.service_process = None
|
|
||||||
self.should_stop = False
|
|
||||||
self.service_thread = None
|
|
||||||
|
|
||||||
def setup_logging(self):
|
|
||||||
"""Setup service logging."""
|
|
||||||
log_dir = os.path.join(os.path.expanduser("~"), "PrintService", "logs")
|
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
|
||||||
|
|
||||||
log_file = os.path.join(log_dir, f"service_wrapper_{time.strftime('%Y%m%d')}.log")
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler(log_file, encoding='utf-8'),
|
|
||||||
logging.StreamHandler(sys.stdout)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def signal_handler(self, signum, frame):
|
|
||||||
"""Handle shutdown signals."""
|
|
||||||
logging.info(f"Service wrapper received signal {signum}")
|
|
||||||
self.stop_service()
|
|
||||||
|
|
||||||
def start_service(self):
|
|
||||||
"""Start the actual print service."""
|
|
||||||
script_dir = Path(__file__).parent
|
|
||||||
service_script = script_dir / "print_service_complete.py"
|
|
||||||
|
|
||||||
if not service_script.exists():
|
|
||||||
logging.error(f"Service script not found: {service_script}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get Python executable path
|
|
||||||
python_exe = sys.executable
|
|
||||||
if not python_exe:
|
|
||||||
python_exe = "python"
|
|
||||||
|
|
||||||
logging.info(f"Starting print service with: {python_exe} {service_script}")
|
|
||||||
|
|
||||||
# Start the service process
|
|
||||||
self.service_process = subprocess.Popen(
|
|
||||||
[python_exe, str(service_script), "--service"],
|
|
||||||
cwd=str(script_dir),
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
text=True,
|
|
||||||
bufsize=1,
|
|
||||||
universal_newlines=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Monitor service output
|
|
||||||
def monitor_output():
|
|
||||||
if self.service_process:
|
|
||||||
for line in iter(self.service_process.stdout.readline, ''):
|
|
||||||
if line:
|
|
||||||
logging.info(f"Service: {line.strip()}")
|
|
||||||
if self.should_stop:
|
|
||||||
break
|
|
||||||
|
|
||||||
self.service_thread = threading.Thread(target=monitor_output, daemon=True)
|
|
||||||
self.service_thread.start()
|
|
||||||
|
|
||||||
logging.info("Print service started successfully")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Failed to start service: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def stop_service(self):
|
|
||||||
"""Stop the print service."""
|
|
||||||
logging.info("Stopping print service...")
|
|
||||||
self.should_stop = True
|
|
||||||
|
|
||||||
if self.service_process:
|
|
||||||
try:
|
|
||||||
# Try graceful shutdown first
|
|
||||||
self.service_process.terminate()
|
|
||||||
|
|
||||||
# Wait up to 10 seconds for graceful shutdown
|
|
||||||
for _ in range(10):
|
|
||||||
if self.service_process.poll() is not None:
|
|
||||||
break
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
# Force kill if still running
|
|
||||||
if self.service_process.poll() is None:
|
|
||||||
logging.warning("Force killing service process")
|
|
||||||
self.service_process.kill()
|
|
||||||
|
|
||||||
self.service_process.wait()
|
|
||||||
logging.info("Print service stopped")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error stopping service: {e}")
|
|
||||||
|
|
||||||
def run_service(self):
|
|
||||||
"""Main service loop."""
|
|
||||||
logging.info("Windows Print Service Wrapper starting...")
|
|
||||||
|
|
||||||
# Setup signal handlers
|
|
||||||
signal.signal(signal.SIGTERM, self.signal_handler)
|
|
||||||
signal.signal(signal.SIGINT, self.signal_handler)
|
|
||||||
if hasattr(signal, 'SIGBREAK'):
|
|
||||||
signal.signal(signal.SIGBREAK, self.signal_handler)
|
|
||||||
|
|
||||||
# Start the actual service
|
|
||||||
if not self.start_service():
|
|
||||||
logging.error("Failed to start print service")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Service main loop - keep running until stopped
|
|
||||||
try:
|
|
||||||
while not self.should_stop:
|
|
||||||
# Check if service process is still running
|
|
||||||
if self.service_process and self.service_process.poll() is not None:
|
|
||||||
logging.warning("Service process died, restarting...")
|
|
||||||
if not self.start_service():
|
|
||||||
logging.error("Failed to restart service")
|
|
||||||
break
|
|
||||||
|
|
||||||
time.sleep(5) # Check every 5 seconds
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logging.info("Service interrupted by user")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Service loop error: {e}")
|
|
||||||
finally:
|
|
||||||
self.stop_service()
|
|
||||||
|
|
||||||
logging.info("Windows Print Service Wrapper stopped")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main entry point."""
|
|
||||||
service_manager = WindowsServiceManager()
|
|
||||||
service_manager.setup_logging()
|
|
||||||
|
|
||||||
# Check command line arguments
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
if sys.argv[1] == "install":
|
|
||||||
logging.info("Service install requested - use install_service_complete.bat instead")
|
|
||||||
return 1
|
|
||||||
elif sys.argv[1] == "start":
|
|
||||||
logging.info("Service start requested")
|
|
||||||
elif sys.argv[1] == "stop":
|
|
||||||
logging.info("Service stop requested")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Run the service
|
|
||||||
return service_manager.run_service()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM Windows Print Service - Troubleshooting and Testing Script
|
|
||||||
REM Use this to test the service before installing it as a Windows service
|
|
||||||
|
|
||||||
echo =========================================
|
|
||||||
echo Windows Print Service - Test Mode
|
|
||||||
echo =========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Set variables
|
|
||||||
set CURRENT_DIR=%~dp0
|
|
||||||
set SERVICE_NAME=QualityPrintService
|
|
||||||
set INSTALL_DIR=C:\QualityPrintService
|
|
||||||
set PYTHON_EXE=%INSTALL_DIR%\python_embedded\python.exe
|
|
||||||
set PYTHON_SCRIPT=%INSTALL_DIR%\print_service_complete.py
|
|
||||||
|
|
||||||
echo [INFO] Testing service in standalone mode...
|
|
||||||
echo [INFO] This helps diagnose issues before installing as Windows service
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check if files exist
|
|
||||||
if not exist "%PYTHON_EXE%" (
|
|
||||||
echo [ERROR] Python not found at: %PYTHON_EXE%
|
|
||||||
echo [INFO] Make sure the service is installed first using install_service_complete.bat
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exist "%PYTHON_SCRIPT%" (
|
|
||||||
echo [ERROR] Service script not found at: %PYTHON_SCRIPT%
|
|
||||||
echo [INFO] Make sure the service is installed first using install_service_complete.bat
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [INFO] Files found, starting service test...
|
|
||||||
echo [INFO] Python: %PYTHON_EXE%
|
|
||||||
echo [INFO] Script: %PYTHON_SCRIPT%
|
|
||||||
echo.
|
|
||||||
echo [INFO] Starting service in test mode...
|
|
||||||
echo [INFO] Press Ctrl+C to stop the service
|
|
||||||
echo [INFO] Service will run on http://localhost:8765
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Change to service directory
|
|
||||||
cd /d "%INSTALL_DIR%"
|
|
||||||
|
|
||||||
REM Start service in standalone mode for testing
|
|
||||||
"%PYTHON_EXE%" "%PYTHON_SCRIPT%" --standalone
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo [INFO] Service test completed.
|
|
||||||
echo [INFO] If the service ran successfully above, you can now:
|
|
||||||
echo [INFO] 1. Test it: http://localhost:8765/health
|
|
||||||
echo [INFO] 2. Start Windows service: net start %SERVICE_NAME%
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo ========================================
|
|
||||||
echo Quality Print Service - Uninstaller
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check for administrator privileges
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% neq 0 (
|
|
||||||
echo ERROR: This uninstaller requires Administrator privileges.
|
|
||||||
echo Please right-click this file and select "Run as administrator"
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Administrator privileges confirmed ✓
|
|
||||||
echo.
|
|
||||||
|
|
||||||
set SERVICE_NAME=QualityPrintService
|
|
||||||
set INSTALL_DIR=C:\QualityPrintService
|
|
||||||
set LOG_DIR=%USERPROFILE%\PrintService
|
|
||||||
|
|
||||||
echo Removing Quality Print Service...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Stop and remove Windows service
|
|
||||||
echo [1/4] Stopping Windows service...
|
|
||||||
sc stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Service stopped ✓
|
|
||||||
timeout /t 3 >nul
|
|
||||||
) else (
|
|
||||||
echo Service was not running
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [2/4] Removing Windows service...
|
|
||||||
sc delete "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Service removed ✓
|
|
||||||
) else (
|
|
||||||
echo Service was not installed or already removed
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Remove scheduled task fallback
|
|
||||||
echo [3/4] Removing scheduled task (if exists)...
|
|
||||||
schtasks /delete /tn "%SERVICE_NAME%" /f >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Scheduled task removed ✓
|
|
||||||
) else (
|
|
||||||
echo No scheduled task found
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Remove installation files
|
|
||||||
echo [4/4] Removing installation files...
|
|
||||||
if exist "%INSTALL_DIR%" (
|
|
||||||
echo Removing installation directory: %INSTALL_DIR%
|
|
||||||
rmdir /s /q "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
if %errorLevel% equ 0 (
|
|
||||||
echo Installation directory removed ✓
|
|
||||||
) else (
|
|
||||||
echo WARNING: Could not remove all installation files
|
|
||||||
echo You may need to manually delete: %INSTALL_DIR%
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo Installation directory not found
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo Uninstallation Complete
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo What was removed:
|
|
||||||
echo ✓ Windows service: %SERVICE_NAME%
|
|
||||||
echo ✓ Installation files: %INSTALL_DIR%
|
|
||||||
echo ✓ Scheduled task (if existed)
|
|
||||||
echo.
|
|
||||||
echo What was kept (optional cleanup):
|
|
||||||
echo - Log files: %LOG_DIR%
|
|
||||||
echo - Chrome extension (manual removal required)
|
|
||||||
echo.
|
|
||||||
echo To completely remove logs:
|
|
||||||
echo rmdir /s /q "%LOG_DIR%"
|
|
||||||
echo.
|
|
||||||
echo To remove Chrome extension:
|
|
||||||
echo Go to chrome://extensions/ and remove "Quality Print Service"
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
Reference in New Issue
Block a user