Querying Zebra printer status using RawPrinterHelper class - printing

I'm using the RawPrinterHelper class from Microsoft, http://support.microsoft.com/kb/322091, to print to a Zebra KR403 printer from C# code, and everything is working fine.
I wish to monitor the status of the printer for paper jams and paper outages. I've found a query that I can send to the printer, "~HQES" or "esc eng 6", that will return everything I need. The problem is that I can not figure out how to send this query to the printer that will allow the printer to respond. The WritePrinter in the RawPrinterHelper class only seems to return a bool or long type.
I also tried using a Win32_printer object to find the PrinterStatus/PrinterState/Errors of the printer. using the following method:
public static string PrinterStateCheck(string szPrinterName)
{
string query = string.Format("SELECT * from Win32_Printer WHERE Name LIKE '%{0}%'", szPrinterName);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection collection = searcher.Get();
string errorName = "";
foreach (ManagementObject printer in collection)
{
int state = Convert.ToInt32(printer["PrinterState"]);
errorName = state.ToString();
}
return errorName;
Utilizing this method, I tried getting the PrinterState, PrinterStatus, and DetectedErrorState, but none of these respond with the information I need. PrinterState always returns a 1024, PrinterStatus always returns a 4, and DetectedErrorState always returns a 2. Though PrinterState did return a 0 on a proper printing and 1024 on a paperjam or media out event for a few prints, now it just returns 1024 on every call.
I have also found that Zebra created their own software for monitoring printers on a network. The problem is our printers are not on a network and are only connected to the client computer via USB. Also, we are hoping to check the status of the printer prior to or after each receipt is printed.
I am hoping there is something from the winspool.Drv that I can use to send raw data to the printer and receive data back from the printer.
Now I'm using the ReadPrinter function of the winspool.Drv, but the function is returning 0 which means that a response from the printer cannot be accessed. This usually means that the printer is not setup for bidirectional communication, but I'm sure that it is. The "Enable bidirectional support" check box is checked in the "Ports" tab of the Printer Properties. Also, the Zebra Setup Utilities can correctly query the printer and receive a response in its Direct Communication window.
Thanks for any advice,
Jeremy

I've done something very similar and i can tell you that there is almost no way at all to monitor print jobs in .NET.
I've gotten close though, doing the following:
Create a "PrinterDiagnosticsFacade" that queries both the .NET PrintQueue object's status and WMI. Neither are always accurate. Merge the data from both to decide the printer's true status.
Adjust the printer's settings so that print jobs stay in the queue. That way you can accurately read the print job's status by doing a WMI query for the print spool jobs. (You can match against the print filename)
Thats how i got close to getting at printer status.
Adding in code to show how its done using the .NET print queue object:
See http://msdn.microsoft.com/en-us/library/system.printing.printqueue.aspx for the printqueue object that starts the code off
PrintQueue me = Queue;
if (me != null)
{
me.Refresh();
//in this if else,
//i purposefully put the ones that error out first
//so that if multiple can be true at the same time
//the errors will definitely take precedence
if (me.HasPaperProblem)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Has paper problem");
}
else if (me.IsDoorOpened)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Door is open");
}
else if (me.IsManualFeedRequired)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Printer needs manually fed");
}
else if (me.IsNotAvailable)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Printer not available");
}
else if (me.IsOutOfMemory)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Printer is out of memory");
}
else if (me.IsOutOfPaper)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Printer is out of paper");
}
else if (me.IsOutputBinFull)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Printer output bin is full");
}
else if (me.IsPaperJammed)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Error, "Error: Paper jam");
}
else if (me.IsOffline)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Offline, "Offline");
}
else if (me.IsBusy)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Busy, "Busy");
}
else if (me.IsInitializing)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Busy, "Initializing");
}
else if (me.IsIOActive)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Busy, "Sending and recieving data");
}
else if (me.IsProcessing)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Busy, "Processing");
}
else if (me.IsWarmingUp)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Busy, "Warming up");
}
else if (me.IsPendingDeletion)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Busy, "Deleting a job");
}
else if (me.IsPaused)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Paused, "Paused");
}
else if (me.IsPrinting)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Printing, "Printing");
}
else if (me.IsPowerSaveOn)
{
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Ready, "In power save mode");
}
else
_SystemDotPrintingStatus = new PrinterStatus(PrinterStatusType.Ready, "Ready");
}

The solution to the problem that we ended up utilizing was to create a WinUSB driver for the printer. This way the device is treated as a USB device. A ZebraUSB object was created using the driver and a method called WriteRead was created. Using the WriteRead method we sent the ~HQES query to the printer and received a response. Sometimes there is some lag time between the query and the response. To combat this, we set the response to a variable and retrieve it using a different method.
I'm not sure of the specifics of the code because I did not code the WinUSB driver, and I do not have access to its code.
The main point of this answer is that we had to create a WinUSB driver for the printer before any of the status queries could work.

Related

Java 8 issues printing PS to network printer

Got a weird question for you. Recently upleveled my old project from java 7(jdk1.7.0_10) to java 8(1.8.0.91.x86_64). In java 7 it printed the post script file with no issues and now it is printing the postscript file as plain text instead of converting the file. This is on a redhat linux environment. Simply I am trying to print a string containing a post script file of a file itself.
Here is my original code
DocFlavor flavor = DocFlavor.INPUT_STREAM.POSTSCRIPT;
PrintService pService = PrintServiceLookup.lookupDefaultPrintService();
// In a field environment, send to the printer
if (System.getenv("USER_DEFINED_RELTOP") == null || pfr.exists()) {
if (pService.getName().isEmpty()) {
LOGGER.error("No printer selected");
} else {
LOGGER.info("Printing to " + pService.getName());
DocPrintJob pj = pService.createPrintJob();
try {
InputStream is = new ByteArrayInputStream(data.getBytes("UTF8"));
Doc doc = new SimpleDoc(is, flavor, null);
PrintJobWatcher pjw = new PrintJobWatcher(pj);
pj.print(doc, null);
pjw.waitForDone();
is.close();
} catch (PrintException | IOException e) {
LOGGER.error(e);
} // try block
} // no printer selected
// Otherwise, send to a file
} else {
That worked fine in java 7, I updated it to the oracle spec found here for java 8.
https://docs.oracle.com/javase/8/docs/api/javax/print/PrintService.html#createPrintJob--
https://docs.oracle.com/javase/8/docs/technotes/guides/jps/spec/printing.fm6.html
DocFlavor psFlavor = DocFlavor.INPUT_STREAM.POSTSCRIPT;
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(MediaSizeName.ISO_A4);
PrintService[] pservices = PrintServiceLookup.lookupPrintServices(psFlavor,
attrs);
File pfr = new File(PFR_INDICATOR);
// In a field environment, send to the printer
if (System.getenv("USER_DEFINED_RELTOP") == null || pfr.exists()) {
//Check we have a printer capable of post script
if (pservices.length > 0) {
LOGGER.info("Printing to " + pservices[0].getName());
DocPrintJob pj = pservices[0].createPrintJob();
try {
InputStream fis = new ByteArrayInputStream(data.getBytes("UTF8"));
//byte[] ba =data.getBytes("UTF8");
Doc doc = new SimpleDoc(fis, psFlavor, null);
LOGGER.info("Doc Flavor " + doc.getDocFlavor());
PrintJobWatcher pjw = new PrintJobWatcher(pj);
LOGGER.info("PrintJob Attributes : " + pj.getAttributes());
pj.print(doc, attrs);
pjw.waitForDone();
fis.close();
} catch (IOException e) {
LOGGER.error(e);
NotificationDialog.show(NotificationDialog.NOTICE_TYPE.ERROR, PRINT_ERROR);
} catch (PrintException e) {
LOGGER.error(e);
}
} else { // no printer selected
This gives me an error java.awt.print.PrinterIOException: java.io.IOException: /usr/bin/lpr: where it looks to not find lpr.
If I keep it the way it was originally (not write to file) it prints the postscript as plain text even if adding the check to check if the printer is post script capable. If I use the new way of printing file I get a lpr not found error. If I print the PS document using the command lpr it converts it as expected and prints fine. If I use lpr -l that doesn't format it prints it document as plain text as well.
Any suggestion/help would be great. I am lost on what to do. I really don't want to convert it to an image and print that.
At a guess I'd say that your printer is an HP or at least PCL + PS printer, not a pure PostScript-only printer.
In that case you generally need to prepend the PostScript with a language selection PJL string. If you don't do this then it usually defaults to PCL and if you don't send any PCL commands (which all begin with 0x1B) then everything is treated as plain ASCII text. That would explain why both your application and lpr -l end up writing text, but lpr itself doesn't (presumably it adds the PJL).
You could try prepending the PostScript file with something like:
%-12345X#PJL JOB
#PJL ENTER LANGUAGE=POSTSCRIPT
NB the first byte there, before the % should be a 0x1b ESC character, but I can't readily paste binary....
Try sending the file with lpr -l if that works then you could try your old printing method.

How to print GeckoWebBrowser to default printer?

I'm trying to print the document in a GeckoWebBrowser, but documentation is limited and to me, it's not at all clear.
I found some code on the internet that at least communicates with the printer (it starts beeping) but I think the printer is asking for a Letter size paper, but it requires the settings to be from print.GetGlobalPrintSettingsAttribute(), if I try my own settings, it gives me a NotImplementedException.
I suspect this is exception is raised on my Gecko.PrinterSettings, because when I swap ps in the print.Print(ps, null);
with the global settings, this exception isn't raised.
The code below:
var domWindow = browser.Window.DomWindow;
var print = Gecko.Xpcom.QueryInterface<Gecko.nsIWebBrowserPrint>(domWindow);
Gecko.PrintSettings ps = new Gecko.PrintSettings();
ps.SetPrintSilentAttribute(false);
ps.SetPrintToFileAttribute(false);
ps.SetShowPrintProgressAttribute(false);
ps.SetOutputFormatAttribute(1); //2 == PDF, so I assume 1 is actual printer
ps.SetPrintBGImagesAttribute(true);
ps.SetStartPageRangeAttribute(1);
ps.SetEndPageRangeAttribute(100);
ps.SetPrintOptions(2, true); // evenPages
ps.SetPrintOptions(1, true); // oddpages
ps.SetEffectivePageSize(768 * 20f, 1024 * 20f);
ps.SetShrinkToFitAttribute(true);
ps.SetScalingAttribute(1.0);
ps.SetPrintBGImagesAttribute(true);
print.Print(ps, null);
Managed to come up with a solution.
What was throwing an exception was
public void SetPersistMarginBoxSettingsAttribute(bool aPersistMarginBoxSettings)
{
throw new NotImplementedException();
}
The above is in PrinterSettings.cs, so it is hard-coded coded to throw a NotImplementedException on a number off attributes (the attribute above isn't the only one hard-coded to throw the exception) as it is not finished(?), so I cannot use it.
However, I can use the GetGlobalSettingsAttribute() as it uses the same interface as PrinterSettings (nsiPrintSettings), so therefore it will have the same attributes all populated for me.
So what can I do is:
I simply copy the GetGlobalPrintSettingsAttribute() into my own printer settings, and adjust them as necessary.
var mySettings = print.GetGlobalPrintSettingsAttribute();
mySettings.SetPrintSilentAttribute(true);
mySettings.SetPrintToFileAttribute(true);
mySettings.SetShowPrintProgressAttribute(false);
mySettings.SetOutputFormatAttribute(2); //2 == PDF
mySettings.SetToFileNameAttribute(#"c:\temp\temp.pdf");
mySettings.SetPrintBGImagesAttribute(true);
mySettings.SetStartPageRangeAttribute(1);
mySettings.SetEndPageRangeAttribute(100);
mySettings.SetPrintOptions(2, true); // evenPages
mySettings.SetPrintOptions(1, true); // oddpages
mySettings.SetShrinkToFitAttribute(true);
mySettings.SetScalingAttribute(1.0);
mySettings.SetPrintBGImagesAttribute(true);
print.Print(mySettings, new Gecko.WebProgressListener());
Please notice I reverted back to PDF for now, in the SetOutputFormatAttribute(2); //2 == PDF
Also changed the print.Print(ps, null); to print.Print(mySettings, new Gecko.WebProgressListener()); but I think having null or Gecko.WebProgressListener() won't make a difference.
Et voilĂ ! - Now, onto the next step, which is to print to a printer, and not as a PDF file.

libspotify does not connect

I'm trying to get familiar with libspotify and I must say the documentation on libspotify is seriously lacking. I've hacked together a small app from the examples but I can't get it to work.
I'm building a C Console Application in Visual Studio 2012. The appkey is correct.
sp_session_config config;
sp_error error;
sp_session *session;
char *blob = NULL;
memset(&config, 0, sizeof(config));
config.api_version = SPOTIFY_API_VERSION;
config.cache_location = "tmp";
config.settings_location = "tmp";
config.application_key = g_appkey;
config.application_key_size = g_appkey_size;
config.user_agent = "SpotiTest";
error = sp_session_create(&config, &session);
if (SP_ERROR_OK != error) {
fprintf(stderr, "failed to create session: %s\n",
sp_error_message(error));
return;
}
error = sp_session_login(session, "USERNAME", "PASSWORD", 1, blob);
if (SP_ERROR_OK != error) {
fprintf(stderr, "failed to log in to Spotify: %s\n",
sp_error_message(error));
sp_session_release(session);
exit(4);
}
sp_connectionstate cs = sp_session_connectionstate (session);
No matter what the username and password (false or correct) sp_session_login always returns SP_ERROR_OK. When I check the connection state with sp_session_connectionstate it always returns SP_CONNECTION_STATE_LOGGED_OUT.
I'm not seeing what I'm doing wrong here and also can't seem to find any relevant answers through the regular channels.
The API is asynchronous. sp_session_login returns immediately, and the login process continues in the background. Look at the examples that come with the API. You need some kind of event loop to call sp_session_process_events, or libspotify won't get any work done, and you probably want to wait until you receive the logged_in callback.

Blackberry: Make a iterative HTTP GET petition using Comms API

I want to store position coords (latitude, longitude) in a table in my MySQL DB querying a url in a way similar to this one: http://locationstore.com/postlocation.php?latitude=var1&longitude=var2 every ten seconds. PHP script works like a charm. Getting the coords in the device ain't no problem either. But making the request to the server is being a hard one. My code goes like this:
public class LocationHTTPSender extends Thread {
for (;;) {
try {
//fetch latest coordinates
coords = this.coords();
//reset url
this.url="http://locationstore.com/postlocation.php";
// create uri
uri = URI.create(this.url);
FireAndForgetDestination ffd = null;
ffd = (FireAndForgetDestination) DestinationFactory.getSenderDestination
("MyContext", uri);
if(ffd == null)
{
ffd = DestinationFactory.createFireAndForgetDestination
(new Context("MyContext"), uri);
}
ByteMessage myMsg = ffd.createByteMessage();
myMsg.setStringPayload("doesnt matter");
((HttpMessage) myMsg).setMethod(HttpMessage.POST);
((HttpMessage) myMsg).setQueryParam("latitude", coords[0]);
((HttpMessage) myMsg).setQueryParam("longitude", coords[1]);
((HttpMessage) myMsg).setQueryParam("user", "1");
int i = ffd.sendNoResponse(myMsg);
ffd.destroy();
System.out.println("Lets sleep for a while..");
Thread.sleep(10000);
System.out.println("woke up");
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("Exception message: " + e.toString());
e.printStackTrace();
}
}
I haven't run this code to test it, but I would be suspicious of this call:
ffd.destroy();
According to the API docs:
Closes the destination. This method cancels all outstanding messages,
discards all responses to those messages (if any), suspends delivery
of all incoming messages, and blocks any future receipt of messages
for this Destination. This method also destroys any persistable
outbound and inbound queues. If Destination uses the Push API, this
method will unregister associated push subscriptions. This method
should be called only during the removal of an application.
So, if you're seeing the first request succeed (at least sometimes), and subsequent requests fail, I would try removing that call to destroy().
See the BlackBerry docs example for this here
Ok so I finally got it running cheerfully. The problem was with the transport selection; even though this example delivered WAP2 (among others) as an available transport in my device, running the network diagnostics tool showed only BIS as available. It also gave me the connection parameters that I needed to append at the end of the URL (;deviceside=false;ConnectionUID=GPMDSEU01;ConnectionType=mds-public). The code ended up like this:
for (;;) {
try {
coords.refreshCoordinates();
this.defaultUrl();
this.setUrl(stringFuncs.replaceAll(this.getUrl(), "%latitude%", coords.getLatitude() + ""));
this.setUrl(stringFuncs.replaceAll(this.getUrl(), "%longitude%", coords.getLongitude() + ""));
cd = cf.getConnection(this.getUrl());
if (cd != null) {
try {
HttpConnection hc = (HttpConnection)cd.getConnection();
final int i = hc.getResponseCode();
hc.close();
} catch (Exception e) {
}
}
//dormir
Thread.sleep(15000);
} catch (Exception e) {
} finally {
//cerrar conexiones
//poner objetos a null
}
Thanks for your help #Nate, it's been very much appreciated.

Close distant USSD session

I am working on a USSD client. Everything works fine except for closing a distant USSD session.
In the specification, we can see the function CUSD:
AT+CUSD=2 should close the USSD session, but this is not really the case.
In fact when I do this sequence:
AT+CUSD='#xxx#',12
AT+CUSD='1',12
I have an open distant connection.
On your handset, you can open a new session by dialing #xxx*#
If I send a:
AT+CUSD='#xxx*#',12
This is not opening a new distant session.
If I send a:
AT+CUSD=2
AT+CUSD='#xxx#'
This is not opening a new distant session.
Do you know how to close a distant session?
I am working with huwaei key E160 and E173 on windows or Linux.
Use in the following way.
AT+CUSD='#xxx#',15
AT+CUSD=2
I am posting this because this is the top result regarding terminating USSD sessions using AT commands and also because the answers are vague.
This is the c# code i used in the end(I was sending the commands to a gsm modem). Hope it helps someone else
SerialPort SendingPort=null;
public string TerminateUssdSession()
{
InitializePort();
//// generate terminate command for modem
string cmd = "";
cmd = "AT+CUSD=2\r";
// send cmd to modem
OpenPort();
SendingPort.Write(cmd);
Thread.Sleep(500);
string response = SendingPort.ReadExisting();
return response;
}
private void InitializePort()
{
if (SendingPort == null)
{
SendingPort = new SerialPort();
SendingPort.PortName = PortName;//put portname here e.g COM5
SendingPort.BaudRate = "112500";
SendingPort.Parity = Parity.None;
SendingPort.DataBits = 8;
SendingPort.StopBits = StopBits.One;
SendingPort.Handshake = Handshake.None;
SendingPort.ReadTimeout = 500;
}
}
private void OpenPort()
{
if (!SendingPort.IsOpen)
{
SendingPort.Open();
}
}

Resources