Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zyan with Windows and Mono on Linux Raspberry #2

Open
guehmann opened this issue Dec 18, 2016 · 22 comments
Open

Zyan with Windows and Mono on Linux Raspberry #2

guehmann opened this issue Dec 18, 2016 · 22 comments

Comments

@guehmann
Copy link

Hi Yallie,

here is a little bit about my Zyan project on Windows, Linux and Linux on Raspberry PI 2/3.
The project is about a Time Tracking Solution, Raspberry PI with Touch Panel 3,5" and RFID reader. Should act as time tracking terminal and optional as area access terminal via a relay or led to simulate access.

All the components are wired on a breadboard and are working fine. Raspberry IO are managed via Raspberry IO General Purpose assembly on NuGet.

The environment is as following:

I have a Windows Zyan Host (Dispatcher) running as service.
I have 4 Zyan Hosts called Agent, 1 on Windows, 1 on Linux Ubuntu with Mono and 2 on Linux Ubuntu with Mono on Raspberry PI 3b.

The Windows Dispatcher is the central program making requests to the clients (agents) and receives request from the clients (agents).

Windows Agent and Linux Agent on Ubuntu are only for testing/debugging purpose. The two Raspberry Linux Agents are the real time tracking and area access terminals.

Raspberry agents have MongoDB locally installed and save the actual time bookings in the database and send a time booking request to the dispatcher (windows service) if available. If not available, later the dispatcher tries to get the bookings from the agents.

Bidirectional Communication and it works.. (Windows <-> Linux/Mono, Windows <-> Raspberry/Mono, Linux/Mono <-> Raspberry/Mono)

But...I have some strange behavior:

Sending request from the dispatcher:

 - to a windows agent
   (works fine within milliseconds)

 - to a linux/ubuntu/mono agent
   (does not work on first call, after a retry the request it works within milliseconds)

 - to a linux/ubuntu/mono/raspberry
   (same behavior as before)

If a make a "QueryAlive" request to all my agents, the first Linux/Mono gets an error with the following message:

Cannot create channel sink to connect to URL /98b09d86_a7b0_4134_91fd_a73b5be56b4a/tuemzdslijppnf8agyjtj7li_3.rem. An appropriate channel has probably not been registered.

If I try it just after this call, every is fine. First call does not succeed.

Every agent has a method to send booking data to the dispatcher, this is the behavior:

 - sending from windows/agent to windows/dispatcher
   (everything is fine within milliseconds)

 - sending from ubuntu/mono real workstation to windows/dispatcher
   (everything is fine within milliseconds)

 - sending from Raspberry/ubuntu/mono to windows/dispatcher
   (works, but the time connection to dispatcher varies from 3 to 12 seconds) 

This is my method to contact the agents/dispatcher...

using (var conn = new ZyanConnection(url, protocol))
{
var proxy = conn.CreateProxy();
var ts = proxy.QueryAlive();
}

Any idea about this behavior???
But it works with the workarounds.

Thanks, Jens.

@yallie
Copy link
Member

yallie commented Dec 18, 2016

Hello Jens!

Thanks a lot for your detailed description! 👍
Great to know it runs on architectures I never tried myself :)

Cannot create channel sink to connect to URL /98b09d86_a7b0_4134_91fd_a73b5be56b4a/tuemzdslijppnf8agyjtj7li_3.rem. An appropriate channel has probably not been registered.

Never seen this error in such situation. The error usually means (surprise!) that we have no registered channel of the given type, so the error's not gonna fix by itself. But in your case it magically goes away on the second call... That's unusual.

Ok, let's try to reproduce it under the debugger. What version of Mono are you using? Does it behave the same on the latest version of Mono? On Windows version of Mono? Can you post a small console program that demonstrates this behavior? You can use this gist as a starting point for your sample (async/await stuff isn't relevant here and can be stripped away to make it simpler).

Regards, Alexey

@yallie
Copy link
Member

yallie commented Dec 18, 2016

(works, but the time connection to dispatcher varies from 3 to 12 seconds)

BTW, do you use encryption? I saw such delays when running encrypted connections on a very low-end CPUs (RSA key generation is computation-intensitive, I think). Try disabling the encryption, perhaps it'll speed things up.

@guehmann
Copy link
Author

guehmann commented Dec 18, 2016

Hello Alexey,

thank you for your help.

I did not turn on encryption explicit. Is it encrypted by default? How can I turn it off?

Environment is:

- Windows 10 / .NET 4.5.1 / Zyan 2.8
- Linux / Ubuntu Mate 16.04.1 LTS / Mono 4.6.1 / Zyan 2.8 / (same on Raspberry)

all programs (dispatcher and agents) using this code:

using System;
using System.Net;
using System.Reflection;
using Anytime.Agent.Model;
using Anytime.Remoting.Contract;
using Zyan.Communication;
using Zyan.Communication.Protocols.Tcp;

namespace Anytime.Agent
{
	class MainClass
	{
		private static ZyanComponentHost Host { get; set; }
		private static AgentConfig settings;
		private static DateTime StartTime = DateTime.Now;

		// Haupteinstiegspunkt...
		public static void Main(string[] args)
		{
			bool done = false;

			try
			{
				string hostname = Dns.GetHostName().ToUpper() ?? "localhost";

				// Host registrieren...
				var protocol = new TcpDuplexServerProtocolSetup(8012);
				Host = new ZyanComponentHost("test-service", protocol);

				Host.RegisterComponent<IAgent, AgentService>();

				// Warten bis Ende per ESC Taste gewünscht wird...
				Console.WriteLine();
				Console.WriteLine("Press ESC to stop the agent, BACKSPACE to simulate booking request...");

				do 
				{
					switch (Console.ReadKey(true).Key)
					{
						case ConsoleKey.Escape:
							done = true;
							break;

						case ConsoleKey.Backspace:
							SendBookingData();
							break;

						default:
							break;
					}

					Console.Write("Incoming: {0} | Outgoing: {1} | Success: {2} | Error: {3} | Fatal: {4}\r",
					             CountI, CountO, CountS, CountE, CountF);
				} while (!done);
			}
			catch (Exception e)
			{
				Console.WriteLine("Error...Reason: {0}", e.Message);
			}
		}

		// Sende Buchungsdaten an den Dispatcher...
		private static void SendBookingData()
		{
			string host = "192.168.22.192";
			int dispatcherPort = 8012;
			string dispatcherService = "test-agent1";
			var protocol = new TcpDuplexClientProtocolSetup();
			var url = protocol.FormatUrl(host, dispatcherPort, dispatcherService);

			// Verbindung zum Host herstellen...
			try
			{
				config = DispatcherConfig.GetConfiguration();
				var protocol = new TcpDuplexClientProtocolSetup();
				var url = protocol.FormatUrl(config.Host, config.Port, config.Service);

				Console.WriteLine("(O:->) Try connecting to dispatcher on host {0}, port {1} with service {2}...",
								  config.Host, config.Port, config.Service);
				
				using (var conn = new ZyanConnection(url, protocol))
				{
					var proxy = conn.CreateProxy<IDispatcher>();
					proxy.SendTerminalData(Dns.GetHostName(), "004711", 0, 10100, DateTime.Now, false);
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("(O:->) Connection to dispatcher failed. Reason: {0}", e.Message);
			}
		}

		// Dienstmethoden des Agent...
		internal class AgentService : IAgent
		{
			// Einfache Anfrage, ob der Agent noch verfügbar ist, und wenn, seit wann...
			public TimeSpan QueryAlive()
			{
				try
				{
					CountI++;
					Console.WriteLine("(I:<-) Request received from dispatcher {0} - QueryAlive()      ", DateTime.Now);
					var ts = DateTime.Now - StartTime;

					return ts;
				}
				catch (Exception e)
				{
					Console.WriteLine("(I:<-) Error in QueryAlive. Reason: {0}", e.Message);

					throw new Exception(e.Message);
				}
			}
		}
	}
}

@yallie
Copy link
Member

yallie commented Dec 18, 2016

I did not turn on encryption explicit. Is it encrypted by default? How can I turn it off?

I believe yes, it's on by default. To turn it off, specify encryption: false in your protocol setup constructors. Using your sample code above, replace:

new TcpDuplexServerProtocolSetup(8012) => new TcpDuplexServerProtocolSetup(8012, null, false);
new TcpDuplexClientProtocolSetup() => TcpDuplexClientProtocolSetup(false);

Linux / Ubuntu Mate 16.04.1 LTS / Mono 4.6.1 / Zyan 2.8 / (same on Raspberry)

Have you tried this code on Mono for Windows?
I'll need some time to set up a linux box for testing.

@guehmann
Copy link
Author

Don't hurry.

I never tried mono on Windows.

I'll implement it with turned of encryption and give you feedback.
Thanks.

@guehmann
Copy link
Author

guehmann commented Dec 19, 2016

Hi Yallie,

you saved my day, great!!!

Turned off encryption an the times from the Raspberry are in milliseconds. So sending and receiving works very fast. For this solution its no problem with encryption disabled. Perhaps on a later stage in Raspberry Development I'll try to turn it on again.

Calling the agents from my dispatcher, it's the same behavior. The first "mono" agent throws the error, but it's no problem. I'll catch this exception and make one retry so i get my results.

Thanks a lot.
Best Jens.
image

image

If this is a little more stable, I'll make long term tests with dispatcher and agent. I'll tell you about.

Small appendum:
If a call my ubuntu/mono agent from a ubuntu/mono/raspberry i do not get the error with "channel sink", the other way round eighter. Must be a Windows .NET Problem perhaps.

@yallie
Copy link
Member

yallie commented Dec 19, 2016

Hello Jens,

glad to hear it helped with the encryption stuff!

Must be a Windows .NET Problem perhaps.

Windows to Windows connections work just fine in lots of my projects,
so I guess it's an interoperability issue between Windows and Mono.
I'm going to set up a virtual Linux box, perhaps using the Vagrant tool
so I can looks closer into the issue.

BTW, if by any chance you yourself use Vagrant and have a ready to use
vagrantfile for your version of Ubuntu and Mono, please let me know.
It'd save me some time figuring out how to set things up.

Regards, Alexey

@guehmann
Copy link
Author

Ok, thank you.

@yallie
Copy link
Member

yallie commented Dec 21, 2016

Hello Jens,

I've installed Vagrant and set up Ubuntu 16.04.1 virtual box.
I couldn't find out how to install specific Mono version, so I've just installed the latest v4.6.2.
Then I've created a minimal console application that can act as a client and a server.

I tried running the app on Windows and connecting from Windows machine.
And I tried running app on my virtual Ubuntu box and connecting from Windows host.
Finally, I tried running the app on Ubuntu and connecting from the same Ubuntu box.
Either way, I see no errors even on my first call:

image

Here is my stripped down application code:

using System;
using System.Linq;
using Zyan.Communication;
using Zyan.Communication.Protocols.Tcp;

class Program
{
	static void Main(string[] args)
	{
		if (!args.Any())
		{
			RunServer();
			return;
		}

		RunClient(args.First());
	}

	public interface IAgent
	{
		void QueryAlive();
	}

	public class Agent : IAgent
	{
		public void QueryAlive()
		{
			Console.WriteLine("QueryAlive!");
		}
	}

	public static void RunServer()
	{
		var proto = new TcpDuplexServerProtocolSetup(8012, null, false);
		using (var host = new ZyanComponentHost("AgentService", proto))
		{
			host.RegisterComponent<IAgent, Agent>();
			Console.WriteLine("Server started. Press Enter to quit");
			Console.WriteLine("Hint: run 'test.exe tcpex://localhost:8012/AgentService' to connect.");
			Console.ReadLine();
		}
	}

	public static void RunClient(string url)
	{
		var proto = new TcpDuplexClientProtocolSetup(false);
		using (var conn = new ZyanConnection(url, proto))
		{
			var proxy = conn.CreateProxy<IAgent>();
			proxy.QueryAlive();

			Console.WriteLine("Connected. Press Enter to quit");
			Console.ReadLine();
		}
	}
}

Please have a look, perhaps I am missing something important.
Try compiling and running the app yourself and tell me if it breaks on the first call.
If it doesn't break out of the box, please try adding some project specifics until it does.

Also, I'm not sure if Mono version is important here, but it could be great if you
considered updating to the latest version of Mono, v4.6.2. Mono becomes gradually
better with each release as they replace their own code with the original MS code.

Please let me know how it goes.
Regards, Alexey

@guehmann
Copy link
Author

Hi Yallie,

thanks a lot for your workout. I'll try it today. Just looked, my mono is 4.2.1 on Ubuntu VMWare Virtual machine and on Raspberry also.
On Windows I do not use mono, only .NET 4.5.2. I'll then try other version.
I'll inform you about it if I'm ready.

Regards Jens.

@yallie
Copy link
Member

yallie commented Dec 22, 2016

Hello Jens,

I didn't tested Mono on Windows, tried only Win.NET <-> Mono.Linux interaction.

UPD.
Tried with Mono/Windows (my version is 4.0.3):
Windows/Mono server — Windows/NET client
Windows/NET server — Windows/Mono client
Windows/Mono server — Windows/Mono client

Everything seems to be fine.

@guehmann
Copy link
Author

Hi Yallie,

just finished testing. Everthing work fine with your code in console application on windows and linux with ubuntu/mono. Event on Ubuntu with Mono Gtk#.

Tested on Windows .NET 4.52 calling Mono Client on Virtual Machine with Ubuntu/Mono 4.6.2 and 4.2.1 (on Raspberry).

Even first call does not produce any error.

I discoverd, the namespace for the remoting interface must exactly match, else i get error the service ist not registerd.

Then i tried my Windows/GUI part to call your code.
I get the same error with 'Channel sink' on first call.
This is an WebApi 2.2 Asp.Net Core 1.0.1 code calling a windows service running topshelf/zyan with async methods. Perhaps this is the problem. But don't care, i have my workaround to intercept it.

This is part of my code in th windows service. Else I think I have the same code like you.

` public static async void AvailabilityCheck(int id)
{
Log2File.Info($"Verfügbarkeit der als aktiv angegebenen Terminals wird ausgeführt...");

        var agents = await AgentFactory.Many(Wrapper.WrapToMongoContext());
        foreach (var agent in agents.Where(a => a.IsEnabled == true))
        {
            try
            {
                var protocol = new TcpDuplexClientProtocolSetup(false);
                var url = protocol.FormatUrl(agent.Host, agent.Port, agent.Service);

                using (var conn = new ZyanConnection(url, protocol))
                {
                    var proxy = conn.CreateProxy<IAgent>();
                    var ts = proxy.QueryAlive();

                    Log2File.Info($"Verbindung zu Terminal '{agent.Name}' auf {agent.Host} erfolgreich hergestellt. Datenverbindung auf Port {agent.Port} " +
                        $"und Servicepunkt '{agent.Service}' erfolgreich eingerichtet. " +
                        $"Das Terminal ist seit {DurationHelper.ElapsedTime(ts.TotalMilliseconds)} aktiv.");
                }
            }
            // Fehlerbehandlung wegen bekanntem Fehler bei Statusabfrage von einem Mono Agent...
            catch (System.Runtime.Remoting.RemotingException re)
            {
                try
                {
                    var message = re.Message;
                    var protocol = new TcpDuplexClientProtocolSetup(false);
                    var url = protocol.FormatUrl(agent.Host, agent.Port, agent.Service);

                    using (var conn = new ZyanConnection(url, protocol))
                    {
                        var proxy = conn.CreateProxy<IAgent>();
                        var ts = proxy.QueryAlive();

                        Log2File.Info($"Verbindung zu Terminal '{agent.Name}' auf {agent.Host} erfolgreich hergestellt. Datenverbindung auf Port {agent.Port} " +
                            $"und Servicepunkt '{agent.Service}' erfolgreich eingerichtet. " +
                            $"Das Terminal ist seit {DurationHelper.ElapsedTime(ts.TotalMilliseconds)} aktiv.");
                    }
                }
                catch (Exception e)
                {
                    Log2File.Error($"Verbindung zu Terminal '{agent.Name}' nicht erfolgreich. Ursache: {e.Message}");
                }
            }
            catch (Exception e)
            {
                Log2File.Error($"Verbindung zu Terminal '{agent.Name}' nicht erfolgreich. Ursache: {e.Message}");
            }
        }

        Messenger.Send("[email protected]",
            "Verfügbarkeitsprüfung Terminal",
            "Die Verfügbarkeitsüberprüfung der aktiven Terminals wurde beendet. " +
            "Überprüfen Sie das Protokoll für nähere Informationen zum Status der einzelnen Terminals.");
    }`

Thank you a lot for your work.
Regards Jens.

@yallie
Copy link
Member

yallie commented Dec 22, 2016

Hello Jens,

Then i tried my Windows/GUI part to call your code.
I get the same error with 'Channel sink' on first call.

Please provide more details.

Where do you run my test application as a server? On Ubuntu virtual box, right?
And you're trying to call it from a Windows GUI application? Is it WinForms? WPF?
Can you post a minimal compileable version of your program that reproduces
the problem when connected to my console server?

PS. I know you have a workaround, but it still feels uneasy as we don't know the cause.
My best guess is that your client app has some non-standard context sink or channel
registered, perhaps by the infrastructure (surely not in your code).

@guehmann
Copy link
Author

Hi Yallie,

sorry about confusing info.

I run your application on windows as server, called by client from ubuntu/virtual machine and ubuntu/raspberry. Everything is ok.

Running the application on Windows/WPF as server and client work fine.

I run your application on ubuntu/virtual machine as server, called by client from ubuntu/raspberry and windows10/console-app. Everything is ok.
Also running the application on ubuntu/raspberry as server, all these constellations work fine.

What is not working is following:
I have an ASP.NET Core front-end calling an ASP.NET Core WebApi 2.2 Method running on IIS. This WebApi method connects as a client to a windows service (topshelf/zyan) running as server. The server itself (dispatcher) should open connection(s) as client to other zyan server over the net. (The dispatcher and the clients have both, server and client functionality implemented to receive requests and send requests by themself.)

image

I'll try to make a small VS2015 project over the day to reproduce the error.

Regards, Jens.

@guehmann
Copy link
Author

guehmann commented Dec 23, 2016

Hi,

First of all wish you a merry christmas and do no work so hard over the days. All the best for you.

Made just a fresh solution with VS2015 attached.

There are 4 projects within this solution:

  • Agent is a simple windows console application running a zyan server on port localhost:8020 waiting for a QueryAlive request coming in.

  • Dispatcher is a windows server application registered a zyan host to receive request from a outside WebApi call or other instances. In one case receiving an "AliveRequest", opening a connection to other zyan host to query an alive request. This service is running on localhost:8012.
    Received this request the service is opening a connection to another zyan host (raspberry) on port (192.68.22.xx:8020) to query the alive status.

  • RemoteObject hold the interface implementation of the remote object.

  • WebApi, is a ASP.NET Core WebApi Controller with a simple method calling via a client call a zyan server to request the QueryAlive of the connected agents.

image

You do not really need an IIS to run the WebApi, just change with command line to the project folder of the WebApi and run 'dotnet run' . This will start a webserver on port 5000

You can use Postman from google to post web request to the api, or just use any explorer to call the simple WebApi Method: 'localhost:5000/zyan/QueryAlive'.

I coded some static ip-adresses in the dispatcher and agent. you must change them met your environment.

So this is just the complete project running on my environment, hope it will work with you.

Best regards, Jens.
NoHopeSolution.zip

@yallie
Copy link
Member

yallie commented Dec 23, 2016

Hello Jens,

Merry Christmas! Thanks a lot for your sample, will look into it.

To be precise:

  1. Does it reproduce your error when running agent app on Mono/Ubuntu?
  2. The Dispatcher app catches the exception when connecting to the agent for the first time?

I don't see the error message about the channel being not registered in your screenhot.

@guehmann
Copy link
Author

guehmann commented Dec 23, 2016

Hi Yallie,

you are right very right. Now working at home I don't have the ability with several virtual machines. Working with my Mac with VM Ware Fusion having 1 Windows 10 and 1 Ubuntu Mono. But cannot start them together because of resources. So the part for the Ubuntu / Rapsberry was not compiled and running yet. I will do this tomorrow.

So for your points:

  • 1 not yet, will compile the client and deploy it to the raspberry as soon as possible and give response.

  • 2 if (point 1 is finished) the dispatched app doesn't catch it, it will throw and error and message.

So sorry about not yet finished complete. I think tomorrow or over tomorrow you' ll see the real result for the Ubuntu/Mono Server.

All the best for you, Jens.

@guehmann
Copy link
Author

guehmann commented Dec 24, 2016

Here is the outstanding result. Now my raspberry is up.

image

Unfortunately it's the same result as in my large project.

image

Also turned off IPv6, result is the same.

Regards Jens.

P.S.:
I'am interesting about your result in calling the first mono server.
I tried some scenarios changing async calls to normal calls and back, calling directly the proxy method. Changing proxy method from inner-class to outer class. but the result is always the same.

The only great difference from the sample console application (which is working) to the new build solution is that the new solutions has already a zyan server connection open, when it calls the raspberry agents. But only on mono/linux workstations. Don't know why, It's beyond my horizon.

@yallie
Copy link
Member

yallie commented Dec 27, 2016 via email

@guehmann
Copy link
Author

Hy Yallie,

Get well first. We can talk in 2 or 3 weeks. My issue is not urgent. On friday i go for a one week vacation, back on 10th january. And yes i thing you'll fix it, when you see the project.

Once again good improvement for you and a happy new year.

Regards Jens.

@yallie
Copy link
Member

yallie commented Feb 10, 2017

Hello Jens,

Sorry for the long pause!

I tried hard to compile your sample, but couldn't get it to work.
When I open your HopeForSolution.sln, Visual Studio displays an error:

image

A couple weeks ago I tried to update all .NET software stack,
I updated my Visual Studio 2015 to Update 3:

image

and installed the dotNetCore SDK v1.0.1-Preview 2.0.3. Still no use.
What exact software versions do I need to compile the WebApi project?
Perhaps I better should recreate it from scratch? Please help me to build it.

Thanks, Alexey

@guehmann
Copy link
Author

guehmann commented Feb 11, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants