tjanczuk / edge

Run .NET and Node.js code in-process on Windows, MacOS, and Linux
http://tjanczuk.github.io/edge
Other
5.41k stars 639 forks source link

How to deal the .NET's event with the nodejs event's handler. #330

Open gentlecolder opened 8 years ago

gentlecolder commented 8 years ago

the problem code is bellow: /the edge code bellow: edged.js/ var stractData = edge.func(function () {/*

async (dynamic input) => {
       Box.DataMonitorValueChanged +=input.DataChangedHandler;
}   

*/});

module.exports.stractData=stractData;

/the nodejs code bellow:/ var databox=require('edged.js');

var util = require('util');

var input = { DataChangedHandler: function (object sender, IList e) { foreach (DataMonitorValueChangedArgs monitorValue in e) {

              Console.log(monitorValue.ToString()+";"+monitorValue.Uid.ToString() + ':' +                     monitorValue.Value + '\n');
            }
}

};

databox.stractData(input);

is there any good solution ?

gentlecolder commented 8 years ago

I tried many times as read the instruction: Func<object, Task> dataChanged=(Func<object, Task>)input.DataChangedHandler; Box.DataMonitorValueChanged +=await dataChanged;
does it is not suport eventhandler since its delegate is async method?

gentlecolder commented 8 years ago

delegate tag can not submit above "Func<object, Task>"

gentlecolder commented 8 years ago

the erro is cannot wait System.Func<object,System. Threading.Tasks.Task> i donot know why?i hope there is a solution which can trans the event result from the .NET to node.js method.

gentlecolder commented 8 years ago

I and i saw .net can handle node events, but node can not handle .net events, that's not fair.

tjanczuk commented 8 years ago

@gentlecolder The functions Edge.js can marshal from JavaScript to C# must have the following signature:

function (data, callback) {
  // ...
  callback(error, result);
}

These functions will then have the shape of the following delegate on the C# side:

Func<object, Task<object>>

If you plan to hook up JavaScript function as a .NET event handler, you will need to provide a wrapper event handler implementation around the marshaled Func<object,Task<object>> in C#.

gentlecolder commented 8 years ago

The debug complied error is: System.Func<ob tem.Threading.Tasks.Task >” can not implicit casting “System.EventHandler<System.Collections.Generic.IList<FBoxC ver.Contract.DataMonitorValueChangedArgs> >

then i changed the Func<object,Task > to Func<object,Task<System.Collections.Generic.IList<FBoxC ver.Contract.DataMonitorValueChangedArgs> > the debug give me the message is "Invalid expression term". So is there and type need to attention when hook up JavaScript function as a .NET event handler

gentlecolder commented 8 years ago

20151013094003

gentlecolder commented 8 years ago

node Code nodecode C# CODE c code

gentlecolder commented 8 years ago

it seamed that the node function can not changed to Eventhandler implicitly, why? doest that means Eventhandler is not a function in C#. I am not good at C#

tjanczuk commented 8 years ago

Edge.js does not marshal arbitrary functions between JavaScript and .NET, the functions must be of appropriate signature. It means that in most cases you will need to write an adapter code or class that changes the intended signature of the function: https://github.com/tjanczuk/edge#how-to-integrate-c-code-into-nodejs-code.

In your case, I believe you need to write a wrapper around *EventHandler type expected in your C# code and the Func<object,Task<object>> exposed by Edge.

gentlecolder commented 8 years ago

above code i have showed wrapped still cannot pass complie

colceagus commented 8 years ago

@tjanczuk Could you please provide a simple example of EdgeJS code (Node.JS) code handling .NET events properly?

I want to start an ID Card Reader application (for 3M CR100 SwipeReader) and I want my (edgejs + nodejs) code to listen to the DocumentReadEvent and DocumentReadErrorEvent from my C# code.

I hope you see this asap.

Please reply to this comment just to see you have seen it and if you can apply some action to this issue commented here.

With all due respect, Daniel.

colceagus commented 8 years ago

@gentlecolder have you been able to assign a javascript event handler to a C# event?

tjanczuk commented 8 years ago

@danielmihai Are you writing .NET application or a Node.js application (i.e. is Node hosted in CLR or vice versa)?

tjanczuk commented 8 years ago

Have a look at https://github.com/tjanczuk/edge/blob/master/samples/209_websocket.js, specifically at how the sendImpl (https://github.com/tjanczuk/edge/blob/master/samples/209_websocket.js#L79) is set up and used to call back to Node.js when something happens in CLR. This example assumes you write a Node.js app and host CLR in it, but the pattern is also applicable if the reverse is true.

colceagus commented 8 years ago

@tjanczuk I want to send a function from JavaScript that acts as an event handler to the c# dll. In c# the invoke function receives a dynamic input as a JavaScript object with the javascript handler function { handleCSeventEventHandler: function(data, callback) { // do something with data (i don't know what data is in this case, most probably the event) callback(error, result); } }

In c# on Invoke, I'll assign the EventHandler to the Event:

public EventHandler DocumentReadEvent; public SwipeReader swipeReader;

.... Invoke (dynamic input) {

this.swipeReader = new SwipeReader(); this.swipeReader.Start();

this.DocumentReadEvent += input.handleCSeventEventHandler;

}

The scenario I'm in : I've written a library that initializes a 3M CR100 SwipeReader, starts listening for SwipeReader events, it uses a Control to invoke event processing (if _threadHelperControl.invokeRequired then _threadHelperControl.invoke(ProcessEvent), I'll post the code below:

private void ProcessEventThreadHelper(MMM.Readers.FullPage.EventCode aEventCode) { try { if (_threadHelperControl.InvokeRequired) { _threadHelperControl.Invoke( new MMM.Readers.FullPage.EventDelegate(ProcessEvent), new object[] {aEventCode} ); } else { ProcessEvent(aEventCode); } } catch (Exception ) { Console.Out.Write("thread helper control failed to invoke ProcessEvent"); } }

private void ProcessEvent(MMM.Readers.FullPage.EventCode aEventCode) { InsertRow((int)aEventCode, aEventCode.ToString(), 0, string.Empty, RowType.Event);

        if (aEventCode == EventCode.DATA_READ)
        {
            if (DocumentReadEvent != null)
            {
                this.DocumentReadEvent(this.resultStr);
            }
        }
    }

In InitializeSwipeAPI i have private int InitialiseSwipeAPI() { MMM.Readers.ErrorCode lErrorCode = MMM.Readers.ErrorCode.NO_ERROR_OCCURRED;

        // Initialise logging and error handling first. The error

handler callback // will receive all error messages generated by the 3M Page Reader SDK MMM.Readers.Modules.Reader.SetErrorHandler( new MMM.Readers.ErrorDelegate(ProcessErrorThreadHelper), IntPtr.Zero ); lErrorCode = MMM.Readers.Modules.Reader.InitialiseLogging( true, 3, -1, "SwipeReader.Net.log" );

        if (lErrorCode == MMM.Readers.ErrorCode.NO_ERROR_OCCURRED)
        {
            // Next load the settings for the Swipe Reader from the ini

files. You can // also modify and save settings back to the ini files using // MMM.Readers.Modules.Reader.SaveSwipeSettings() lErrorCode = MMM.Readers.Modules.Reader.LoadSwipeSettings( ref swipeSettings );

        }

        if (lErrorCode == MMM.Readers.ErrorCode.NO_ERROR_OCCURRED)
        {
            // Initialise the Swipe Reader. Data and events will be

sent back in a // non-blocking fashion through the callbacks provided // // Thread helper delegates are used to avoid thread-safety issues, // particularly with .NET framework 2.0 lErrorCode = MMM.Readers.Modules.Swipe.Initialise( swipeSettings, new MMM.Readers.Modules.Swipe.DataDelegate(ProcessDataThreadHelper), new MMM.Readers.FullPage.EventDelegate(ProcessEventThreadHelper) ); }

        if (lErrorCode != MMM.Readers.ErrorCode.NO_ERROR_OCCURRED)
        {
            InsertRow(
                (int)lErrorCode,
                lErrorCode.ToString(),
                0,
                "Failed to initialise Swipe Reader API",
                RowType.Error
            );
            return (int)lErrorCode;
        }

return 1; }

How can I keep the DLL alive in nodejs so that it continually runs and listens for event triggering from the CR100 SwipeReader? How can I assign a function from nodejs to c# eventhandler to handle the event? How can the CLR communicate with v8 to send the event to my nodejs module?

Kind Regards, Daniel.

On 21 ian. 2016, at 22:27, Tomasz Janczuk notifications@github.com wrote:

Have a look at https://github.com/tjanczuk/edge/blob/master/samples/209_websocket.js, specifically at how the sendImpl ( https://github.com/tjanczuk/edge/blob/master/samples/209_websocket.js#L79) is set up and used to call back to Node.js when something happens in CLR. This example assumes you write a Node.js app and host CLR in it, but the pattern is also applicable if the reverse is true.

— Reply to this email directly or view it on GitHub https://github.com/tjanczuk/edge/issues/330#issuecomment-173698254.

tjanczuk commented 8 years ago

This line of code will require some extra work:

this.DocumentReadEvent += input.handleCSeventEventHandler;

The input.handleCSeventEventHandler is of type Func<object,Task<object>> and as such will need to be wrapped in an adapter class that is of type expected by DocumentReadEvent. Within this wrapper class you will also likely need to covert the actual event data into a structure that can be passed as an object to handleCSeventEventHandler. It can be an anonymous object. You may also find out that passing the event directly works, but I would not count on that.

colceagus commented 8 years ago

Hi @tjanczuk ,

I managed to get it working by integrating this example https://github.com/tjanczuk/edge/blob/master/samples/209_websocket.js and sending the function to the DLL.

public abstract class NetWebSocket
    {
        private Func<object, Task<object>>  SendImpl { get; set; }

        protected NetWebSocket(Func<object, Task<object>> sendImpl)
        {
            this.SendImpl = sendImpl;
        }

        protected abstract Task ReceiveAsync(string message);

        public Func<object, Task<object>> ReceiveImpl
        {
            get
            {
                return async (input) =>
                {
                    Console.Out.WriteLine(input);
                    await this.ReceiveAsync((string) input);
                    return Task.FromResult<object>(null);
                };
            }
        }

        protected async Task SendAsync(string message)
        {
            await this.SendImpl(message);
            return;
        }
    }

    public class MyNetWebSocketImpl : NetWebSocket
    {
        public CHello module;
        private string JSONCodelineDataRepr = "not set";

        public MyNetWebSocketImpl(Func<object, Task<object>> sendImpl) : base(sendImpl)
        {
            // do other stuff after calling the super class constructor  
            module = new CHello();
            module.DocumentReadEvent += this.DocumentReadEventHandler;
            module.DocumentReadErrorEvent += this.DocumentReadErrorEventHandler;
            // uncomment after the websocket communication works
            module.Start();
        }

        protected override async Task ReceiveAsync(string message)
        {
            // not really needed because only the NodeJS Server listens to C# .NET Server messages
            Console.WriteLine(message);
            if (message.Equals("shutdown"))
            {
                module.Close();
            }
            // not necessary (can comment the send function call)
            // if I eventually receive a message, respond with the JSON representation of the Patient ID Card
            await this.SendAsync("I received a message from you, but I'll ignore it and send you the Patient" +
                                 " ID Card Data instead.. I'm a fish, so start phishing! PersonData = " +
                                 JSONCodelineDataRepr);
            return;
        }

        private async void DocumentReadEventHandler(string args)
        {
            this.JSONCodelineDataRepr = args;
            await this.SendAsync(args);
        }

        private async void DocumentReadErrorEventHandler(string args)
        {
            await this.SendAsync(args);
        }
    }

    public class Startup
    {

        public static MyNetWebSocketImpl ws;

        public async Task<object> Invoke(Func<object, Task<object>> sendImpl)
        {
            ws = new MyNetWebSocketImpl(sendImpl);

            return ws.ReceiveImpl;
        }
    }

(function(e,d,g,e){

    var edge = require('edge'),
        http = require('http'),
        WebSocketServer = require('ws').Server,
        swipe = edge.func('./dlls/ActiveXCOM.dll');

    var server = http.createServer(function(req,res){
        res.writeHead(200, {'Content-Type' : 'text/html'});
        res.end((
            function () { /*
                <!DOCTYPE html>
                <html>
                <head>
                </head>
                <body>
                    <div id='jsonOutput'>
                    </div>
                    <script>
                        var ws = new WebSocket('ws://' + window.document.location.host);

                        ws.onmessage = function (event) {
                            console.log(event.data);
                            var div = document.getElementById('jsonOutput');
                            div.innerHTML = event.data;
                        }

                        ws.onopen = function (event) {
                            // send something to the server
                            ws.send('I am the client from the browser calling you, the NodeJS WebSocketServer!');
                        }

                        ws.onclose = function (event) {
                            alert('websocket closing');
                        }

                        window.onbeforeunload = function myonbeforeUnload() {
                            return "Are you sure?";
                        }

                        window.onunload = function myonunload() {
                            confirm('Are you really sure?');
                            ws.close();
                            return "Are you really sure?";
                        }
                    </script>
                </body>
                </html>
            */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]);
    });

    var wss = new WebSocketServer({server: server});

    wss.on('connection', function(ws) {
        var sendImpl = function (message, callback) {
            console.log(message);
            ws.send(message);
            callback();
        };

        var receiveHandler = swipe(sendImpl, true); 

        ws.on('message', function (message) {
            receiveHandler(message);
        });

        ws.on('close', function close(){
            console.log('****************************The client disconnected!');
            receiveHandler('shutdown');
            delete receiveHandler;
        });
    });

    server.listen(process.env.PORT || 8080);
    module.exports = this;
})();

This kind of does what it should. Thank you very very much! My new question arises if this can be done without this abstractified websocket connection, just by giving the invoke a function(input, callback) { //do something; callback(error, result); } and the edgejs func returns a Func<object, Task> function as a ReceiveImpl on the clientside node application (in this case, client is the node app, server is the C# DLL, but both are server and client at the same time <=> C# is a client-server, Node App is a client-server as well).

^:)^ There are no words to thank you enough!

Would you like to include this example of handling C# events in Javascript (Node) to the samples folder? I would be glad to offer the full implementation to have a mini contribution to EdgeJS (Samples only, for now, who knows what might happen in the future), if you agree :D.

colceagus commented 8 years ago

@tjanczuk Another question popped into my mind, if it's possible to convert this into a client-side javascript module and have things packed like nw.js does ? What do you think?

Thank you again for your support, examples and wonderful library! ^:)^ :bow:

colceagus commented 8 years ago

@tjanczuk Any news?

skybedy commented 7 years ago

Hi @danielmihai , can I ask, if you solved this your situation? I deal with a similar problem as you, only I need to handle C# events from RFID reader.
Thanks for a reaction.

colceagus commented 7 years ago

If you keep the same pattern as in the example I've written, it doesn't matter what you expose. Keep the same pattern.

darylltee commented 7 years ago

@danielmihai , Hi, how did you get your swipe reader working. I'm working with my fingerprint scanner but no luck in listening for events. Thanks for you responce. ` // Activate capture handler

currentReader.On_Captured += new `Reader.CaptureCallback(OnCaptured);

`

colceagus commented 7 years ago

I would first make a test program in c#, then adapt it to work for edge, then write the DLL I really need for my app

Daniel M. Colceag

On 20 Apr 2017, at 13:26, Daryll Tee notifications@github.com wrote:

@danielmihai , Hi, how did you get your swipe reader working. I'm working with my fingerprint scanner but no luck in listening for events. Thanks for you responce. // Activate capture handler currentReader.On_Captured += newReader.CaptureCallback(OnCaptured);``

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.