using System; using System.Globalization; using System.Web; using System.Drawing.Imaging; using GenLogic; namespace ProcessHandler { public class ProcessHandler : IHttpHandler { private static Object init_lock = new Object(); private static GlgHttpRequestProcessor request_processor = null; private static GlgObject viewport = null; /* Demonstrates updating the drawing using either tags (true) or resources (false). */ static bool UseTags = true; static String drawing_name = "process2.g"; static String app_path = "ProcessHandler"; // Global simulated data used by all handler instances. static GlgProcessDemoData data = new GlgProcessDemoData(); /////////////////////////////////////////////////////////////////// public bool IsReusable { get { return false; } } /////////////////////////////////////////////////////////////////// public void ProcessRequest( HttpContext context ) { lock( init_lock ) { /* First time only: create GlgHttpRequestProcessor that properly handles ASP.NET synchronization context. ProcessRequestData is registered as a custom application-specific method used by the request processor to process HTTP requests. */ if( request_processor == null ) request_processor = new GlgHttpRequestProcessor( ProcessRequestData ); } /* Request processor will invoke ProcessRequestData method to handle application-specific logic. */ GlgHttpRequestData request_data = request_processor.ProcessRequest( context ); /* Returned data contains an image or HTML text response. If there were request processing errors, the errors will be reported in the HTML output and returned data will be null. */ if( request_data != null ) { if( request_data.image != null ) { // Save image. context.Response.ContentType = "image/png"; request_data.image.Save( context.Response.OutputStream, ImageFormat.Png ); } else if( request_data.html_response != null ) // Write html response (tooltip or dialog data). context.Response.Write( request_data.html_response ); else context.Response.Write( "Error: Unexpected response.
"); } } /////////////////////////////////////////////////////////////////// // A custom method invoked to process HTTP context. // // The HTTP context is passed in the request_data.context field. // The generated image is assigned to the data.image field. // The data.custom_data field may be used to pass additional // custom data to the ProcessRequest method. // // The GlgObject.Error method may be used in the code to flag errors. // Any errors will be reported in the generated HTML by the default // GLG error handler. // // A custom GLG error handler may be set using the // GlgObject.SetErrorHandler method inside this method. // The request_data.got_errors field may be set to true to flag errors // in case if a custom error handler is used. /////////////////////////////////////////////////////////////////// void ProcessRequestData( GlgHttpRequestData request_data ) { HttpRequest request = request_data.context.Request; String action = GetStringParameter( request, "action", "GetImage" ); if( action.Equals( "GetDialogData" ) ) { // Simply return requested dialog data: no drawing required. ProcessDialogData( request_data ); return; } /* The rest of actions (GetImage, ProcessEvent) require a drawing - load it (if first time) and update with data. */ // Get requested width/height of the image: need for all other actions. int width = GetIntegerParameter( request, "width", 500 ); int height = GetIntegerParameter( request, "height", 400 ); // Limit max. size to avoid running out of heap space creating an image. if( width > 1000 ) width = 1000; if( height > 1000 ) height = 1000; /* Load the drawing just once and share it between all instances of this HTTP handler. */ if( viewport == null ) { GlgObject.Init(); String path; String dir = request_data.context.Server.MapPath( "~" ); if( dir.IndexOf( app_path ) == -1 ) path = dir + app_path + "\\" + drawing_name; else path = dir + "\\" + drawing_name; viewport = LoadDrawing( path ); viewport.SetImageSize( width, height ); viewport.SetupHierarchy(); // Setup to prepare to receive data } else // Already loaded, reuse the drawing. viewport.SetImageSize( width, height ); ShowPipes( request ); // Show pipes and flow lines if requested. data.UpdateProcessData(); // Get the new data values. UpdateDrawingWithData(); // Updates drawing with current data. // Setup after data update to prepare to generate image. viewport.SetupHierarchy(); // Main action: Generate Image. if( action.Equals( "GetImage" ) ) { request_data.image = viewport.CreateImage( null ); } // Secondary action: ProcessEvent. else if( action.Equals( "ProcessEvent" ) ) { String event_type = GetStringParameter( request, "event_type", "" ); String selection_info = null; GlgSelectionEventType selection_type; if( event_type.Equals( "MouseClick" ) ) // Get selected object { // Find object with the MouseClick custom event. selection_type = GlgSelectionEventType.CLICK_SELECTION; } else if( event_type.Equals( "Tooltip" ) ) // Get object's tooltip { // Find object with the TooltipString property. selection_type = GlgSelectionEventType.TOOLTIP_SELECTION; } else { selection_type = 0; selection_info = "Unsupported event_type"; } if( selection_type != 0 ) { // Get x and y coordinates of the mouse click. int x = GetIntegerParameter( request, "x", -1 ); int y = GetIntegerParameter( request, "y", -1 ); // Selection rectangle around the mouse click. GlgCube click_box = new GlgCube(); int selection_sensitivity = 3; // in pixels click_box.p1.x = x - selection_sensitivity; click_box.p1.y = y - selection_sensitivity; click_box.p2.x = x + selection_sensitivity; click_box.p2.y = y + selection_sensitivity; // Find selected object with MouseClick custom event attached. if( x > 0 && y > 0 ) { // Select using MouseClick custom events. GlgObject selection_message = null; selection_message = GlgObject.CreateSelectionMessage( viewport, click_box, viewport, selection_type, 1 ); if( selection_message != null ) switch( selection_type ) { default: case GlgSelectionEventType.CLICK_SELECTION: // Return object name. selection_info = selection_message.GetSResource( "Object/Name" ); break; case GlgSelectionEventType.TOOLTIP_SELECTION: /* Return tooltip string, which is an event label of the tooltip action message. */ selection_info = selection_message.GetSResource( "EventLabel" ); break; } } else selection_info = "Invalid x/y coordinates."; } request_data.html_response = ( selection_info == null ? "None" : selection_info ); } else Error( "Unsupported action!" ); } ///////////////////////////////////////////////////////////////// // Helper methods ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// int GetIntegerParameter( HttpRequest request, String name, int default_value ) { String parameter_string = request.QueryString[ name ]; if( parameter_string == null || parameter_string.Equals( "" ) ) return default_value; try { return int.Parse( parameter_string, CultureInfo.InvariantCulture ); } catch( Exception ) { Error( "Invalid parameter value for: " + name + " = " + parameter_string ); return default_value; } } ///////////////////////////////////////////////////////////////// String GetStringParameter( HttpRequest request, String name, String default_value ) { String parameter_string = request.QueryString[ name ]; if( parameter_string == null ) return default_value; else return parameter_string; } ///////////////////////////////////////////////////////////////// // Can be used only in ProcessRequestData() or methods // originating from it. ///////////////////////////////////////////////////////////////// void Error( String message ) { GlgObject.Error( GlgErrorType.USER_ERROR, message, null ); } ///////////////////////////////////////////////////////////////// GlgObject LoadDrawing( String drawing_path ) { GlgObject drawing; drawing = GlgObject.LoadWidget( drawing_path, GlgMediumType.FILE ); if( drawing == null ) { Error( "Can't load drawing: " + drawing_path ); return null; } // Disable viewport border in the image: let html define it if needed. drawing.SetDResource( "LineWidth", 0.0 ); return drawing; } ////////////////////////////////////////////////////////////////////////// // Updates drawing state with data. ////////////////////////////////////////////////////////////////////////// void UpdateDrawingWithData() { // The drawing can be updated using either tags or resources. if( UseTags ) UpdateProcessTags(); else UpdateProcessResources(); } ////////////////////////////////////////////////////////////////////////// // Updates drawing using resources. ////////////////////////////////////////////////////////////////////////// public void UpdateProcessTags() { viewport.SetDTag( "SolventValveValue", data.SolventValve, true ); viewport.SetDTag( "SteamValveValue", data.SteamValve, true ); viewport.SetDTag( "CoolingValveValue", data.CoolingValve, true ); viewport.SetDTag( "WaterValveValue", data.WaterValve, true ); viewport.SetDTag( "SteamTemperature", data.SteamTemperature, true ); viewport.SetDTag( "HeaterTemperature", data.HeaterTemperature, true ); viewport.SetDTag( "BeforePreHeaterTemperature", data.BeforePreHeaterTemperature, true ); viewport.SetDTag( "PreHeaterTemperature", data.PreHeaterTemperature, true ); viewport.SetDTag( "AfterPreHeaterTemperature", data.AfterPreHeaterTemperature, true ); viewport.SetDTag( "CoolingTemperature", data.CoolingTemperature, true ); viewport.SetDTag( "HeaterLevel", data.HeaterLevel, true ); viewport.SetDTag( "WaterLevel", data.WaterLevel, true ); viewport.SetDTag( "HeaterAlarm", data.HeaterAlarm ? 1.0 : 0.0, true ); viewport.SetDTag( "WaterAlarm", data.WaterAlarm ? 1.0 : 0.0, true ); /* Pass if_changed=false to move the chart even if the value did not change. The rest of resources use true to update them only if their values changed. */ viewport.SetDTag( "PlotValueEntryPoint", data.HeaterTemperature, false ); viewport.SetDTag( "PressureValue", 5.0 * data.HeaterPressure, true ); } ////////////////////////////////////////////////////////////////////////// // Updates drawing using resources. ////////////////////////////////////////////////////////////////////////// public void UpdateProcessResources() { viewport.SetDResource( "SolventValve/Angle", data.SolventValve, true ); viewport.SetDResource( "SteamValve/Angle", data.SteamValve, true ); viewport.SetDResource( "CoolingValve/Angle", data.CoolingValve, true ); viewport.SetDResource( "WaterValve/Angle", data.WaterValve, true ); viewport.SetDResource( "Heater/SteamTemperature", data.SteamTemperature, true ); viewport.SetDResource( "Heater/HeaterTemperature", data.HeaterTemperature, true ); viewport.SetDResource( "BeforePreHeaterTemperature", data.BeforePreHeaterTemperature, true ); viewport.SetDResource( "PreHeaterTemperature", data.PreHeaterTemperature, true ); viewport.SetDResource( "AfterPreHeaterTemperature", data.AfterPreHeaterTemperature, true ); viewport.SetDResource( "CoolingTemperature", data.CoolingTemperature, true ); viewport.SetDResource( "Heater/HeaterLevel", data.HeaterLevel, true ); viewport.SetDResource( "WaterSeparator/WaterLevel", data.WaterLevel, true ); viewport.SetDResource( "HeaterAlarm", data.HeaterAlarm ? 1.0 : 0.0, true ); viewport.SetDResource( "WaterAlarm", data.WaterAlarm ? 1.0 : 0.0, true ); /* Pass if_changed=false to move the chart even if the value did not change. The rest of resources use true to update them only if their values changed. */ viewport.SetDResource( "ChartVP/Chart/Plots/Plot#0/ValueEntryPoint", data.HeaterTemperature, false ); viewport.SetDResource( "PressureGauge/Value", 5.0 * data.HeaterPressure, true ); } ///////////////////////////////////////////////////////////////// // Shows pipes and flow lines if requested. // Constraints in the drawing take care of updating associated // toggle buttons when the pipe or flow line display state is // changed. The last parameter of SetDResource() is set to true // to update the drawing only if the resource value gets changed. ///////////////////////////////////////////////////////////////// void ShowPipes( HttpRequest request ) { // Show pipes if requested, default 0. int show_pipes = GetIntegerParameter( request, "show_pipes", 0 ); viewport.SetDResource( "3DPipes/Visibility", (double)show_pipes, true ); // Show flow if requested, default 1. int show_flow = GetIntegerParameter( request, "show_flow", 1 ); viewport.SetDResource( "FlowGroup/Visibility", (double)show_flow, true ); } ///////////////////////////////////////////////////////////////// // Returns data for a requested dialog. ///////////////////////////////////////////////////////////////// void ProcessDialogData( GlgHttpRequestData request_data ) { HttpRequest request = request_data.context.Request; String dialog_type = GetStringParameter( request, "dialog_type", "None" ); if( dialog_type.Equals( "Heater" ) ) { request_data.html_response = "Solvent Heater
" + "Level: " + Format( data.HeaterLevel * 100.0 ) + " %
" + "Pressure: " + Format( data.HeaterPressure * 5.0 ) + " ATM.
" + "Temperature: " + Format( 50.0 + data.HeaterTemperature * 100.0 ) + " C\u00B0"; } else if( dialog_type.Equals( "WaterSeparator" ) ) { request_data.html_response = "Water Heater
" + "Level: " + Format( data.WaterLevel * 100.0 ) + " %
" + "Temperature: " + Format( 50 + data.CoolingTemperature * 30.0 ) + " C\u00B0"; } else if( dialog_type.Equals( "SolventValve" ) ) { request_data.html_response = "Solvent Valve
" + "Open: " + Format( data.SolventValve * 100.0 ) + " %"; } else if( dialog_type.Equals( "SteamValve" ) ) { request_data.html_response = "Steam Valve
" + "Open: " + Format( data.SteamValve * 100.0 ) + " %"; } else if( dialog_type.Equals( "CoolingValve" ) ) { request_data.html_response = "Cooling Valve
" + "Open: " + Format( data.CoolingValve * 100.0 ) + " %"; } else if( dialog_type.Equals( "WaterValve" ) ) { request_data.html_response = "Water Valve
" + "Open: " + Format( data.WaterValve * 100.0 ) + " %"; } else request_data.html_response = "None"; } String Format( double value ) { return GlgObject.Printf( "%.2f", value ); } } }