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}")
|
||||
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'])
|
||||
def get_order_data(order_id):
|
||||
"""Get specific order data for preview"""
|
||||
|
||||
@@ -204,6 +204,25 @@
|
||||
<div style="margin-bottom: 5px;">Creates sequential labels based on quantity</div>
|
||||
<small>(e.g., CP00000711-001 to CP00000711-063)</small>
|
||||
</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> <!-- Data Preview Card -->
|
||||
<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
|
||||
addPDFGenerationHandler();
|
||||
|
||||
// Initialize print service
|
||||
initializePrintService();
|
||||
|
||||
// Auto-select first row
|
||||
setTimeout(() => {
|
||||
const firstRow = document.querySelector('.print-module-table tbody tr');
|
||||
@@ -396,6 +418,96 @@ function updateLabelPreview(order) {
|
||||
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
|
||||
// Labels are generated as PDF files for universal compatibility
|
||||
function addPDFGenerationHandler() {
|
||||
@@ -463,67 +575,114 @@ function handleDirectPrint(selectedRow) {
|
||||
printerName = prompt('Enter your printer name:');
|
||||
if (!printerName) return;
|
||||
} 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
|
||||
const cells = selectedRow.querySelectorAll('td');
|
||||
const orderData = {
|
||||
order_id: parseInt(orderId),
|
||||
comanda_productie: cells[1].textContent.trim(),
|
||||
cod_articol: cells[2].textContent.trim(),
|
||||
descr_com_prod: cells[3].textContent.trim(),
|
||||
cantitate: parseInt(cells[4].textContent.trim()),
|
||||
data_livrare: cells[5].textContent.trim(),
|
||||
dimensiune: cells[6].textContent.trim(),
|
||||
com_achiz_client: cells[7].textContent.trim(),
|
||||
nr_linie_com_client: cells[8].textContent.trim(),
|
||||
customer_name: cells[9].textContent.trim(),
|
||||
customer_article_number: cells[10].textContent.trim()
|
||||
};
|
||||
|
||||
// Print each label individually
|
||||
for (let i = 1; i <= quantity; i++) {
|
||||
const labelData = {
|
||||
...orderData,
|
||||
sequential_number: `${orderData.comanda_productie}-${i.toString().padStart(3, '0')}`,
|
||||
current_label: i,
|
||||
total_labels: quantity
|
||||
};
|
||||
|
||||
// Encode data as base64 JSON
|
||||
const jsonString = JSON.stringify(labelData);
|
||||
const base64Data = btoa(unescape(encodeURIComponent(jsonString)));
|
||||
|
||||
// Create custom protocol URL
|
||||
const printUrl = `recticel-print://${encodeURIComponent(printerName)}/${base64Data}`;
|
||||
|
||||
console.log(`Printing label ${i}/${quantity}: ${printUrl.substring(0, 100)}...`);
|
||||
|
||||
// Trigger direct print via custom protocol
|
||||
try {
|
||||
window.location.href = printUrl;
|
||||
} catch (error) {
|
||||
console.error(`Failed to print label ${i}:`, error);
|
||||
alert(`❌ Failed to print label ${i}/${quantity}. Please check if the print service is installed.`);
|
||||
return;
|
||||
// Check if print service is available
|
||||
checkPrintServiceStatus()
|
||||
.then(serviceStatus => {
|
||||
if (!serviceStatus) {
|
||||
throw new Error('Print service is not available');
|
||||
}
|
||||
|
||||
// Generate PDF first for printing
|
||||
return generatePrintablePDF(orderId, true); // true for paper-saving mode
|
||||
})
|
||||
.then(pdfUrl => {
|
||||
// Send PDF to print service
|
||||
const printData = {
|
||||
pdf_url: pdfUrl,
|
||||
printer: printerName,
|
||||
copies: 1,
|
||||
order_id: parseInt(orderId),
|
||||
paper_saving: true
|
||||
};
|
||||
|
||||
return fetch('http://localhost:8899/print-pdf', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(printData)
|
||||
});
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Print service error: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
alert(`✅ Successfully sent ${quantity} labels to printer!\n📊 Order: ${prodOrder}\n🖨️ Printer: ${printerName || 'Default'}\n📄 Job ID: ${result.job_id || 'N/A'}`);
|
||||
|
||||
// Update database status and refresh table
|
||||
updatePrintedStatus(orderId);
|
||||
} 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';
|
||||
});
|
||||
}
|
||||
|
||||
// Small delay between labels to prevent overwhelming the printer
|
||||
if (i < quantity) {
|
||||
setTimeout(() => {}, 100);
|
||||
return false;
|
||||
})
|
||||
.catch(error => {
|
||||
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'
|
||||
}
|
||||
}
|
||||
|
||||
// Show success message
|
||||
setTimeout(() => {
|
||||
alert(`✅ Sent ${quantity} labels to printer: ${printerName}\n📊 Order: ${prodOrder}`);
|
||||
|
||||
// Update database status and refresh table
|
||||
updatePrintedStatus(orderId);
|
||||
}, 500);
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to generate PDF: ${response.status}`);
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
// Create a temporary URL for the PDF
|
||||
return URL.createObjectURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
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