private void checkBoxEnableHeadPower_CheckedChanged(object sender, EventArgs e) { if (inSetHeadPower) { return; } inSetHeadPower = true; if (status.Connected) { eRET rVal = PrinterInterfaceCLS.PiSetHeadPower(checkBoxEnableHeadPower.Checked ? 1 : 0); if (rVal != eRET.RVAL_OK) { MessageBox.Show("PiSetHeadPower failed with " + rVal.ToString() + "\n\nPlease check that all PCC cards are connected and have completed initialisation", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk); checkBoxEnableHeadPower.Checked = false; } else { // Prevent further PiSetHeadPower commands being sent until the status reports HeadPowerIdle checkBoxEnableHeadPower.Enabled = false; } } inSetHeadPower = false; }
/// <summary> /// Disconnect from the Meteor Printer Interface if currently connected. /// Should be called before the application exits. /// </summary> public void Disconnect() { if (Connected) { PrinterInterfaceCLS.PiAbort(); WaitNotBusy(); PrinterInterfaceCLS.PiClosePrinter(); Connected = false; } }
/// <summary> /// Set up the printer prior to starting a print job. /// /// PiSetAndValidateParam blocks until the parameters have been successfully set (or have failed to set /// - e.g. if there is an out of range value). /// /// This must be used here in preference to the asynchronous method PiSetParam to guarantee that the /// values are set in Meteor before the print job is started. /// /// </summary> /// <returns>Success / failure</returns> private bool SetupPrinter() { if (PrinterInterfaceCLS.PiSetAndValidateParam((int)eCFGPARAM.CCP_PRINT_CLOCK_HZ, UserPrintClock) != eRET.RVAL_OK) { return(false); } if (PrinterInterfaceCLS.PiSetAndValidateParam((int)eCFGPARAM.CCP_BITS_PER_PIXEL, UserBitsPerPixel) != eRET.RVAL_OK) { return(false); } return(true); }
/// <summary> /// Polls the Printer Interface until it finishes the previous control command /// Use timeoutms to specify how long to wait in ms, default is 10000ms. /// For example, the Printer Interface will remain busy after a /// PiAbort until the abort has completed in Meteor. /// Note that the interface does not become busy as a result of a call to /// PiSendCommand. /// </summary> /// <param name="timeoutms">Timeout in milliseconds. Defaults to ten seconds.</param> public bool WaitNotBusy(int timeoutms = 10000) { while (PrinterInterfaceCLS.PiIsBusy()) { Thread.Sleep(100); if ((timeoutms -= 100) <= 0) { return(false); } } return(true); }
/// <summary> /// Abort any in-progress print job /// </summary> private void AbortPrintJob() { // No longer starting a job bJobStarting = false; bJobAborting = true; // Update display status and refresh, as the abort can take a few seconds buttonStopPrint.Enabled = false; buttonStopPrint.Refresh(); textBoxStatus.Text = PRINTER_STATUS.ABORTING.ToString(); textBoxStatus.Refresh(); // Send the abort command to Meteor. This will halt any in-progress // print, and clear out all print buffers PrinterInterfaceCLS.PiAbort(); // Wait until the abort has completed Cursor.Current = Cursors.WaitCursor; status.WaitNotBusy(); Cursor.Current = Cursors.Default; }
/// <summary> /// Query the head and auxiliary temperatures for the given pcc / head /// </summary> /// <param name="pccnum">pcc index (1-max)</param> /// <param name="hnum">head index (1-max)</param> /// <param name="temperature1">set with the head temperature</param> /// <param name="temperature2">set with the aux temperature</param> /// <returns>success / failure</returns> public bool GetHeadTemperature(int pccnum, int hnum, out int temperature1, out int temperature2) { TAppHeadStatus HeadStatus; if (PrinterInterfaceCLS.PiGetHeadStatus(pccnum, hnum, out HeadStatus) == eRET.RVAL_OK) { // Convert from 0.1 degrees to degrees and round temperature1 = (int)(((double)HeadStatus.Temperature1 / 10.0) + 0.5); temperature2 = (int)(((double)HeadStatus.Temperature2 / 10.0) + 0.5); return(true); } else { temperature1 = 0; temperature2 = 0; return(false); } }
/// <summary> /// See if we can access PrinterInterfaceCLS. If not, look in the registry and see where Meteor /// is installed, add this to the path, and try again. /// </summary> static private bool DoLocatePrinterInterface() { // See if we can call into the Printer Interface. This will drill down into the // underlying unmanaged PrinterInterface.dll. try { PrinterInterfaceCLS.PiGetBuildNumber(); } // If both the managed (PrinterInterfaceCLS.dll) and unmanaged (PrinterInterface.dll) components // were found, there's nothing to do. If just the managed component is found, we must add the // API folder to the environment path of the current process. catch (DllNotFoundException) { string apipath = GetMeteorAPIPath(); if (apipath != null) { string envpath = Environment.GetEnvironmentVariable("PATH"); Console.WriteLine("MeteorPath: Adding '" + apipath + "' to local environment PATH"); envpath = apipath + ";" + envpath; Environment.SetEnvironmentVariable("PATH", envpath); // Try again try { PrinterInterfaceCLS.PiGetBuildNumber(); } catch (Exception e) { Console.WriteLine("MeteorPath: Exception + " + e.Message); return(false); } } else { Console.WriteLine("MeteorPath: Install folder not found"); return(false); } } // Unhandled exception catch (Exception e) { Console.WriteLine("MeteorPath: Exception + " + e.Message); return(false); } return(true); }
/// <summary> /// Check whether any of the PCCs in the system are in the process of turning head /// power on or off. Should only be called when all required PCCs are connected. /// Sets the value of HeadPowerIdle. /// </summary> private void CheckHeadPowerIdle() { for (int pccNum = 1; pccNum <= PccsRequired; pccNum++) { TAppPccStatus pccStatus; eRET rVal = PrinterInterfaceCLS.PiGetPccStatus(pccNum, out pccStatus); if (rVal != eRET.RVAL_OK) { HeadPowerIdle = false; return; } if ((pccStatus.bmStatusBits2 & Bmps.BMPS2_HEAD_POWER_IN_PROGRESS) != 0) { HeadPowerIdle = false; return; } } HeadPowerIdle = true; }
/// <summary> /// Send the print job to Meteor. When the method returns, all print data has been /// sent to Meteor - however, it has not necessarily all been sent to the hardware. /// </summary> /// <returns>Success / failure</returns> public eRET Start() { eRET rVal; // Meteor command to start a print job int[] StartJobCmd = new int[] { (int)CtrlCmdIds.PCMD_STARTJOB, // Command ID 4, // Number of DWORD parameters jobid, // Job ID (int)eJOBTYPE.JT_PRELOAD, // This job uses the preload data path (int)eRES.RES_HIGH, // Print at full resolution image.GetDocWidth() + 2 // Needed for Left-To-Right printing only }; // A start job command can fail if there is an existing print job // ready or printing in Meteor, or if a previous print job is still // aborting. The sequencing of the main form's control enables should // guarantee that thie never happens in this application. if ((rVal = PrinterInterfaceCLS.PiSendCommand(StartJobCmd)) != eRET.RVAL_OK) { return(rVal); } // The start document command specifies the number of discrete copies // of the image which are required // int[] StartDocCmd = new int[] { (int)CtrlCmdIds.PCMD_STARTPDOC, // Command ID 1, // DWORD parameter count repeatmode == (REPEAT_MODE.DISCRETE) ? copies : 1 }; if ((rVal = PrinterInterfaceCLS.PiSendCommand(StartDocCmd)) != eRET.RVAL_OK) { return(rVal); } // For seamless image repeats using the prelod data path, PCMD_REPEAT // must be sent after PCMD_STARTPDOC and before the image data. // if (copies > 1 && repeatmode == REPEAT_MODE.SEAMLESS) { int[] RepeatCmd = new int[] { (int)CtrlCmdIds.PCMD_REPEAT, // Command ID 1, // DWORD parameter cound copies }; if ((rVal = PrinterInterfaceCLS.PiSendCommand(RepeatCmd)) != eRET.RVAL_OK) { return(rVal); } } // PCMD_BIGIMAGE must be used if the application needs to pass images // which exceed 60MB in size to Meteor as one buffer. (An alternative // is for the application to split up the data into smaller images, // each of which can used PCMD_IMAGE). // // The image data is sent through the Printer Interface to the Meteor // Print Engine in chunks. The application must continually call // PiSendCommand with the same buffer while the Print Engine // returns RVAL_FULL. // // Note that it is necessary to fix the location of the image command // in memory while carrying out this sequence, to prevent the garbage // collector from relocating the buffer (theoretically possible, but // highly unlikely) between successive PiSendCommand calls. // int[] ImageCmd = image.GetBigImageCommand(ytop, bpp); unsafe { fixed(int *pImageCmd = ImageCmd) { do { rVal = PrinterInterfaceCLS.PiSendCommand(ImageCmd); } while (rVal == eRET.RVAL_FULL); if (rVal != eRET.RVAL_OK) { return(rVal); } } } int[] EndDocCmd = new int[] { (int)CtrlCmdIds.PCMD_ENDDOC, 0 }; if ((rVal = PrinterInterfaceCLS.PiSendCommand(EndDocCmd)) != eRET.RVAL_OK) { return(rVal); } int[] EndJobCmd = new int[] { (int)CtrlCmdIds.PCMD_ENDJOB, 0 }; if ((rVal = PrinterInterfaceCLS.PiSendCommand(EndJobCmd)) != eRET.RVAL_OK) { return(rVal); } return(eRET.RVAL_OK); }
/// <summary> /// Called periodically to handle Meteor initialisation and status /// </summary> private void HandleMeteorStatus() { // Get the Meteor status / open a connection to Meteor if one doesn't // already exist PRINTER_STATUS Status = status.GetStatus(); if (Status == PRINTER_STATUS.READY || Status == PRINTER_STATUS.PRINTING || Status == PRINTER_STATUS.DISCONNECTED) { bJobStarting = false; } else { bJobAborting = false; } latestPrinterStatus = Status; // Update the head/aux temperature setpoints if the values in the inteface // have been changed if (status.Connected) { if (lastSetHeadTemperature != UserHeadTemperature) { // Set target head temperature globally (PiSetParam with pcc = 0, head = 0) if (PrinterInterfaceCLS.PiSetParam((int)eCFGPARAM.CCP_HEAD_TEMP, UserHeadTemperature) == eRET.RVAL_OK) { lastSetHeadTemperature = UserHeadTemperature; } } if (lastSetAuxTemperature != UserAuxTemperature) { // Set target auxiliary temperature globally (PiSetParam with pcc = 0, head = 0) if (PrinterInterfaceCLS.PiSetParam((int)eCFGPARAM.CCP_AUX_TEMP, UserAuxTemperature) == eRET.RVAL_OK) // Set globally { lastSetAuxTemperature = UserAuxTemperature; } } if (lastSupportedBppBitmask != status.SupportedBppBitmask) { lastSupportedBppBitmask = status.SupportedBppBitmask; EnableBppRadioButtons(); } // Meteor will reject a PiSetHeadPower command if any of the PCCs are still in the process // of changing the head power status if (checkBoxEnableHeadPower.Enabled != status.HeadPowerIdle) { checkBoxEnableHeadPower.Enabled = status.HeadPowerIdle; } // If the Meteor head power state is stable, check that we're displaying the correct state if (status.HeadPowerIdle) { if (status.HeadPowerEnabled != checkBoxEnableHeadPower.Checked) { inSetHeadPower = true; checkBoxEnableHeadPower.Checked = status.HeadPowerEnabled; inSetHeadPower = false; } } } else { lastSetHeadTemperature = -1; lastSetAuxTemperature = -1; } // Update the enabled state of the controls to reflect the Meteor status EnableControls(); // Update the status text if (bJobStarting) { textBoxStatus.Text = PRINTER_STATUS.LOADING.ToString(); } else if (bJobAborting) { textBoxStatus.Text = PRINTER_STATUS.ABORTING.ToString(); } else { textBoxStatus.Text = Status.ToString(); } }
/// <summary> /// Get the status from Meteor. Also attempts to open a connection to Meteor every /// 2 seconds if the application is not currently connected. /// </summary> /// <returns></returns> public PRINTER_STATUS GetStatus() { TAppStatus AppStatus; eRET rVal; if (!Connected) { if (Environment.TickCount < tickLastConnectAttempt + CONNECT_INTERVAL_MS) { return(PRINTER_STATUS.DISCONNECTED); } tickLastConnectAttempt = Environment.TickCount; rVal = PrinterInterfaceCLS.PiOpenPrinter(); // RVAL_OK means the Meteor Printer Interface was successfully opened. // // RVAL_EXISTS means that the Printer Interface is already open in this // process. // // All other return values mean we have failed to open the printer // interface - the most likely reasons are either the Meteor Print Engine // is not running, or the Meteor Printer Interface is already open in another // print application. // if (rVal != eRET.RVAL_OK && rVal != eRET.RVAL_EXISTS) { return(PRINTER_STATUS.DISCONNECTED); } Connected = true; // Send an abort command in case the application which previously used // Meteor left a print job running. This is not done if Meteor is // currently initialising the hardware, as in this case it should // not be necessary, and can take a significant amount of time to complete. rVal = PrinterInterfaceCLS.PiGetPrnStatus(out AppStatus); if (rVal == eRET.RVAL_OK && AppStatus.PrinterState != ePRINTERSTATE.MPS_INITIALIZING) { PrinterInterfaceCLS.PiAbort(); } // Wait until the abort has completed. While an abort is in progress // the printer interface is busy, and status requests will fail. WaitNotBusy(); } // Request the Meteor printer status. Provided the Print Engine is // still running, this method should never fail in a single threaded // printer application. In a more complex application which uses // multiple threads to can communicate with the Print Engine, it // is possible (though rare) for the status request to time out if // the Printer Interface is busy servicing another request. // rVal = PrinterInterfaceCLS.PiGetPrnStatus(out AppStatus); if (rVal != eRET.RVAL_OK) { if (Connected) { PrinterInterfaceCLS.PiClosePrinter(); Connected = false; } return(PRINTER_STATUS.DISCONNECTED); } // Store the bitmask indicating which bits-per-pixel are valid for the current head type SupportedBppBitmask = AppStatus.SupportedBppBitmask; // The Meteor Print Engine state goes into IDLE after all connecteded PCCs have finished // initialising. // // Here we trap the case of the Meteor configuration file listing more PCCs than are // currently attached. // PccsRequired = AppStatus.PccsRequired; if (PccsRequired != AppStatus.PccsAttached) { return(PRINTER_STATUS.WAITING_FOR_PCC); } // Protect against the application acting on a brief status transition to IDLE ePRINTERSTATE PrinterState = AppStatus.PrinterState; if (AppStatus.PrinterState == ePRINTERSTATE.MPS_IDLE) { if (idleConfCount < IDLE_CONF) { PrinterState = LastMeteorState; idleConfCount++; } } else { idleConfCount = 0; } // Track last known stable printer state LastMeteorState = PrinterState; // Find out whether Meteor is currently turning head power on or off CheckHeadPowerIdle(); // Is at least one head powered on HeadPowerEnabled = (AppStatus.HeadPowerState == eHEADPOWERSTATE.HPS_HEAD); // Convert from the Meteor printer status into the application status switch (PrinterState) { // There is no hardware connected. case ePRINTERSTATE.MPS_DISCONNECTED: return(PRINTER_STATUS.WAITING_FOR_PCC); // The PCC hardware has connected to the Print Engine but has not yet been // initialised case ePRINTERSTATE.MPS_CONNECTED: return(PRINTER_STATUS.INITIALISING); // The Print Engine is initialised and waiting for a print job to start case ePRINTERSTATE.MPS_IDLE: return(PRINTER_STATUS.IDLE); // The next print job is ready to start case ePRINTERSTATE.MPS_READY: return(PRINTER_STATUS.READY); // Meteor is actively printing case ePRINTERSTATE.MPS_PRINTING: return(PRINTER_STATUS.PRINTING); // The Print Engine is downloading FPGA code to the PCCs case ePRINTERSTATE.MPS_INITIALIZING: return(PRINTER_STATUS.INITIALISING); // The PCC FPGA has completed programming and is starting up case ePRINTERSTATE.MPS_STARTUP: return(PRINTER_STATUS.INITIALISING); // Fault condition - more information can be found by looking at the // values in the status registers. For the purposes of this application // we just report the generic error; the register values can be viewed // in Monitor case ePRINTERSTATE.MPS_FAULT: return(PRINTER_STATUS.ERROR); // Image data is being loaded case ePRINTERSTATE.MPS_LOADING: return(PRINTER_STATUS.LOADING); // Should never get here default: return(PRINTER_STATUS.ERROR); } }