Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

MICROS Interface 1

Status
Not open for further replies.

Moregelen

Programmer
Sep 10, 2012
1,222
US
So I've decided to roll a reusable micros interface class, which is working pretty well:

Code:
[COLOR=#0000FF]using[/color] System;
[COLOR=#0000FF]using[/color] System.Windows.Forms;
[COLOR=#0000FF]using[/color] CommonLibraries.Google;
[COLOR=#0000FF]using[/color] CommonLibraries.Micros.InterfaceComponents;
 
[[COLOR=#0000FF]assembly[/color]: [COLOR=#2B91AF]DefaultNamespace[/color]([COLOR=#A31515]"MicrosTestInterface"[/color])]
[COLOR=#0000FF]namespace[/color] MicrosTestInterface
{
    [COLOR=#0000FF]public[/color] [COLOR=#0000FF]partial[/color] [COLOR=#0000FF]class[/color] [COLOR=#2B91AF]Form1[/color] : [COLOR=#2B91AF]Form[/color]
    {
        [COLOR=#0000FF]private[/color] [COLOR=#2B91AF]MicrosInterfaceServer[/color] _interface;
 
        [COLOR=#0000FF]public[/color] Form1()
        {
            InitializeComponent();
 
            [COLOR=#008000]//start a new interface server on port 32400 which requires outgoing message name to be TEST[/color]
            _interface = [COLOR=#0000FF]new[/color] [COLOR=#2B91AF]MicrosInterfaceServer[/color](32400, [COLOR=#A31515]"TEST"[/color], HandleClient);
            _interface.Start();
        }
        [COLOR=#0000FF]private[/color] [COLOR=#2B91AF]MicrosMessage[/color] HandleClient([COLOR=#2B91AF]MicrosClient[/color] client)
        {
            [COLOR=#008000]//get the first message received from the client[/color]
            [COLOR=#2B91AF]MicrosMessage[/color] message = client.MicrosMessage;
            AddMessage([COLOR=#A31515]"Request from workstation: "[/color] + message.UWS);
            [COLOR=#008000]//check if we received our only valid command[/color]
            [COLOR=#0000FF]if[/color] (message.Command == [COLOR=#A31515]"get_distance"[/color])
            {
                [COLOR=#0000FF]if[/color] (message.DataParts.Length != 2)
                    [COLOR=#0000FF]return[/color] message.CreateReply([COLOR=#A31515]"error"[/color], [COLOR=#A31515]"Invalid Number of Parameters"[/color]); [COLOR=#008000]//we weren't provided with two strings, so send back an rxmsg: error event[/color]
 
                [COLOR=#0000FF]string[/color] add1 = message.DataParts[0];
                [COLOR=#0000FF]string[/color] add2 = message.DataParts[1];
 
                AddMessage([COLOR=#A31515]"Address 1: "[/color] + add1);
                AddMessage([COLOR=#A31515]"Address 2: "[/color] + add2);
 
                [COLOR=#2B91AF]DistanceResult[/color] result = [COLOR=#2B91AF]GoogleDirections[/color].GetDistance(add1, add2, [COLOR=#2B91AF]DistanceUnits[/color].Imperial, [COLOR=#2B91AF]DistanceMethod[/color].Driving);
                [COLOR=#0000FF]if[/color] (result == [COLOR=#0000FF]null[/color]) [COLOR=#008000]//send back rxmsg: error letting them know we couldn't get to the Google API - usually means firewall[/color]
                    [COLOR=#0000FF]return[/color] message.CreateReply([COLOR=#A31515]"error"[/color], [COLOR=#A31515]"Failed to get distance between addresses"[/color]);
                [COLOR=#0000FF]if[/color] (result.Status != [COLOR=#2B91AF]DirectionsStatus[/color].OK) [COLOR=#008000]//send back rxmsg: error letting them know the error the Google API returned[/color]
                    [COLOR=#0000FF]return[/color] message.CreateReply([COLOR=#A31515]"error"[/color], [COLOR=#A31515]"Error Occured: "[/color] + result.Status.ToString());
 
                AddMessage([COLOR=#A31515]"Distance: "[/color] + result.Distance);
                AddMessage([COLOR=#0000FF]string[/color].Empty); [COLOR=#008000]//send back the distance; its possible to end up with a negative when getting distances between coordinates so abs it[/color]
                [COLOR=#0000FF]return[/color] message.CreateReply([COLOR=#A31515]"show_distance"[/color], [COLOR=#2B91AF]Math[/color].Abs(result.Distance));
            }
            [COLOR=#008000]//didn't receive a get_distance request, so send be an rxmsg: error event[/color]
            [COLOR=#0000FF]return[/color] message.CreateReply([COLOR=#A31515]"error"[/color], [COLOR=#A31515]"Unknown Interface Command"[/color]);
        }
        [COLOR=#0000FF]private[/color] [COLOR=#0000FF]void[/color] AddMessage([COLOR=#0000FF]string[/color] message)
        {
            [COLOR=#0000FF]if[/color] (InvokeRequired)
            {
                Invoke(([COLOR=#2B91AF]MethodInvoker[/color])(() => { AddMessage(message); }));
                [COLOR=#0000FF]return[/color];
            }
            textBox1.Text += message + [COLOR=#2B91AF]Environment[/color].NewLine;
            textBox1.SelectionStart = textBox1.Text.Length;
            textBox1.ScrollToCaret();
        }
    }
}

The issue I'm trying to solve.. does Micros interface messages support any kind of back and forth? Micros seems to always close the connection immediately after getting the first reply. Is there any way to have a .. conversation, I guess.. with the workstation?
 
You want to use a "Keep Alive" type of message.

What version of Micros are you running, and are you using the Micros Interfaces with SIM scripts?
 
it appears you may have written your own C# class library namespace CommonLibraries.Micros.InterfaceComponents with a MicrosInterfaceServer class in it?
Is this a wrapper for some other Micros dll or is it custom code? (or is this a Micros provided library that I'm not aware of?)
 
There isn't a problem with the keep alive - micros does send that to make sure the interface is still open, and I'm also not having problems keeping the TCP port open during the communication round. What I mean is if there is any way to basically have a back and forth within the ISL script without creating a new socket each time. It seems whenever I send a new txmsg it creates a new TCP connection; when I reply to the message, Micros then shuts the socket down. It goes through the process of creating a brand new connection every time a message is sent, which has enough overheard to actually be noticeable. I don't currently have any particular need to be able to do this right now, just trying to see if I'm missing something.

@wewantphil - it is a custom library that I wrote which basically creates a TcpListener and handles everything in the background - receiving the ISL formatted message, turning it into something useful, and creating properly formatted replies. It also silently handles the status checks the IFS does periodically. Just makes it a lot easier to rapidly create a new service when I don't have to worry about all the formatting and handling of socket connections going on in the background every time. AFAIK the only library that Micros provides is their COM interface to the POS Api. I have a lot of other stuff rolled into the library under different namespaces - things like PInvokes I really, really, really don't want to work out all over again, etc.

The SIM I'm using for testing:

Code:
event inq : 1

	var add1 : A200
	input add1, "Enter Address 1"
	var add2 : A200
	input add2, "Enter Address 2"
	
	txmsg "get_distance", add1, add2
	waitforrxmsg
	
endevent

event rxmsg : show_distance

	var distance : $12
	rxmsg distance
	var message : A200
	format message as "Distance: ", distance, " mi"
	infomessage message
	
endevent

event rxmsg : error

	var error_msg : A200
	rxmsg error_msg
	exitwitherror error_msg

endevent
 
nice work. I wrote a similar interface listener that uses async/await and runs either as a Windows Service or command line if testing (haven't had reason to implement PInvoke yet, scary). AFAIK you wouldn't be able to do what you're wanting to do unless the client supported it (like long polling or SignalR). Pretty sure the Micros client expects one request and one reply per connection, otherwise how would the client know that the result was complete and ready to display back to the user?
 
Right. I was just wondering if SIM had some kind of undocumented thing were you could do something like:

start xmsg

txmsg "command", "commmand"​
rxmsg result​
txmsg "response to result"​
rxmsg lastBitINeed​

end xmsg

I know that is probably hoping for waaaay too much, but at least it isn't a scenario I can see coming up vary often. TCP connections usually have communications going back and forth, so I was hoping Micros might have had some way to do that. If not, I guess I can stick with my current mechanism which expects the connection to be terminated after sending its reply. I was just hoping there was some way to avoid the overhead of completely recreating the socket even if my next communication with interface is directly after my first. Even if all it was was Micros having some kind of idle timeout on the connection if it wasn't utilized again within a certain amount of time.

I've written a number of integrations with Micros, but I never bothered to package it up before - I basically redid all of the communications each time. It honestly isn't that difficult to setup and handle a TCP listener and then talk to Micros, but the above code certainly makes for some nice brevity, which allows one to quickly get into the meat of the project, so I'm glad I did it. And yeah.. PInvokes. Hah. I wrote a CAL package recently for Win32 devices to do certain things we needed setup - things we were previously wasting a lot of time doing manually. I ended up using so many damned PInvokes (what a headache) in the thing, most related to doing an impersonation of the console session so that I could launch a GUI program from SYSTEM under their user, that I might as well have written it using C++.
 
@moregelen, to my knowledge and understanding it creates the new pipe each time to keep up with PCI and stop from something hitting the service/server and causing issues. I haven't seen a away to tell Micros to use the same one. Sorry, but thats a nice interface listener.
 
Oh well! I supposed I could sort of see that being a PCI concern, though I would never ever run a PMS like that with the connection going out to the net. We've always used some kind of locally running service that then does the work. At least it is easy enough to do with Simphony
 
Man oh man do I feel stupid right now. I started really thinking about it, and I re-read some of the docs.. why the heck is Micros sending keep alive messages if it closes the TCP socket every time it receives a reply? Why would you bother writing an async/await style interface if Micros enforces a single message round like that. Well, because duh. It doesn't. It keeps the socket open for use. I have no idea where my assumption that it creates a brand new socket on every communication came from, but I feel awfully foolish now. Did a quick re-write so that the socket is left open rather than disposing of it every time I send a reply, and I've noticed a definite improvement now that Micros isn't having to deal with my fool ass closing the socket every time I send a reply. I really can't even remember where I got that assumption from...

@wewantphil - Out of curiosity, which version of .NET did you use for your interface? I love the concept of async/await and I've used it on a few other things before (mainly things like writing large files), but I've always limited my code to .NET 3.5 because it is the only version I can guarantee to be installed on a Micros box. I tackled async/await once in 3.5 and found it a pain to work with without the Tasks class. I admit that I'm a total novice with network communications; I've tried to account for things like half open connections and receiving things like data injections on the service (made pretty easy by the rigid message formatting and the fact that micros defines a maximum data size), but I'm not really sure if I'm covering all my bases.

I've rewritten the service to use asynchronous reading and writing, but I'm wondering if you have any thoughts or suggestions to add? You sound like you might be a bit more familiar with working with network communications in C#!
 
@morgelen: I was wondering why you were saying you were closing the socket. by doing so, you stop listening... so you are correct with your rewrite, leave the socket open. But then, as you may know, you have to worry about contention. Once a workstation connects to that TCP socket, you have a blocking connection and your program is going to have to finish servicing that request, send the response, and close the connection before the next request comes in. That next request will, of course, wait around for the designated timeout if the socket is not yet available. But if we're worried about performance, that's a huge performance hit and not a good end user experience. This is where async/await can really help. By using that pattern, your code will put the request on a non-blocking thread while it does the work and then feed the results back to the client after the await. Then you can configure how many concurrent connections you're willing to service at any given moment in time (I think I put mine at 20).
I know how you feel with sticking with older .NET versions. I have to do that with some existing projects as well. Specific to Micros, I had written a custom web service to run on the micros server in it's IIS install, but that became to restrictive, staying with the older version. So now I just have a separate box that hosts the custom code and just uses ODBC to communicate with the database.
I should add that the code that uses ODBC is just for reporting purposes, it is not for interacting in real time with micros client requests. For that I use SVC SIM and have not yet had to go beyond what SVC offers.
Async/Await shipped in .NET 4.5 so that is the minimum requirement for that feature. You can of course write async code (not async/await) in earlier versions of .NET but it is clunky and tedious.
This makes me think about the possibility of writing the Interface in .NET Core 1.x though, since it does not require installing the full framework on the Micros server. I know it's still RC2 so you may not want to put it in production but it will RTM very soon.
 
Right, right! I'm fairly certain I got it right in my rewrite. I'm more used to just starting up new threads, but it seems that kind of overhead really should be unnecessary.

MicrosClient setup to use standard asynchronous receiving and sending on the socket, and then raises an event when it gets a message which the MicrosIterface hooks into. To prevent blocking, I raise the event asynchronously:


Code:
[COLOR=#0000FF]private[/color] [COLOR=#0000FF]void[/color] OnReceive([COLOR=#2B91AF]IAsyncResult[/color] ar)
{
    [COLOR=#0000FF]try[/color] {
        _CanTimeout = [COLOR=#0000FF]false[/color];
        [COLOR=#0000FF]if[/color] (_Socket == [COLOR=#0000FF]null[/color] || !_Socket.Connected)
        {
            Dispose();
            [COLOR=#0000FF]return[/color];
        }
        [COLOR=#0000FF]int[/color] Ret = _Socket.EndReceive(ar);
        [COLOR=#0000FF]if[/color] (Ret == 0)
        {
            Dispose();
            [COLOR=#0000FF]return[/color];
        }
        _StringBuffer += [COLOR=#2B91AF]Encoding[/color].ASCII.GetString(_Buffer, 0, Ret);
        [COLOR=#0000FF]if[/color] (!_StringBuffer.Contains([COLOR=#2B91AF]MicrosMessage[/color].StartOfHeader)) [COLOR=#008000]//no data should ever be lacking the start of header. If it is, it didn't come from micros[/color]
        {
            Dispose();
            [COLOR=#0000FF]return[/color];
        }
 
        [COLOR=#0000FF]if[/color] (_StringBuffer.Contains([COLOR=#2B91AF]MicrosMessage[/color].EndOfTransmission))
        {
            [COLOR=#0000FF]int[/color] index_SOH = _StringBuffer.IndexOf([COLOR=#2B91AF]MicrosMessage[/color].StartOfHeader);
            [COLOR=#0000FF]int[/color] index_EOT = _StringBuffer.IndexOf([COLOR=#2B91AF]MicrosMessage[/color].EndOfTransmission);
            [COLOR=#0000FF]string[/color] message = _StringBuffer.Substring(0, index_EOT + 1);
            _StringBuffer = message.Length == _StringBuffer.Length ? [COLOR=#0000FF]string[/color].Empty : _StringBuffer.Substring(index_EOT + 1);
            [COLOR=#2B91AF]MicrosMessage[/color] m = [COLOR=#2B91AF]MicrosMessage[/color].FromString(message);
            [COLOR=#008000]//invoke the listeners async so that we can get on with receiving the next message while the interface does whatever it needs to do in order to generate a reply[/color]
            [COLOR=#0000FF]var[/color] receivedHandlers = MessageReceived.GetInvocationList();
            [COLOR=#0000FF]for[/color] ([COLOR=#0000FF]int[/color] i = 0; i < receivedHandlers.Count(); i++)
            {
                [COLOR=#0000FF]var[/color] method = ([COLOR=#2B91AF]MessageEventHandler[/color])receivedHandlers[i];
                method.BeginInvoke([COLOR=#0000FF]this[/color], m, [COLOR=#0000FF]new[/color] [COLOR=#2B91AF]AsyncCallback[/color](MessageHandled), [COLOR=#0000FF]null[/color]);
            }
        }
        [COLOR=#0000FF]else[/color] [COLOR=#0000FF]if[/color] ([COLOR=#2B91AF]Encoding[/color].ASCII.GetByteCount(_StringBuffer) > 32000) [COLOR=#008000]//micros will only ever send 32K of data, so if we haven't gotten an EOT and are past 32K, it ain't Micros sending it[/color]
        {
            Dispose();
            [COLOR=#0000FF]return[/color];
        }
 
        _Socket.BeginReceive(_Buffer, 0, _Buffer.Length, [COLOR=#2B91AF]SocketFlags[/color].None, [COLOR=#0000FF]new[/color] [COLOR=#2B91AF]AsyncCallback[/color](OnReceive), _Socket);
        _CanTimeout = [COLOR=#0000FF]true[/color];
        SetTimeout();
    } [COLOR=#0000FF]catch[/color] ([COLOR=#2B91AF]SocketException[/color] ex)
    {
        [COLOR=#2B91AF]Log[/color].DebugException([COLOR=#0000FF]this[/color], ex);
        Dispose();
    }
}
 
nice work moregelen! you had mentioned earlier that you have other classes that call some unmanaged code through PInvokes. Wondering if you modeled this rewrite on the success of that work? it looks like the basic pattern you have here could work for such a thing. Understanding that you may have a requirement to stay with an older version of .NET, the one thing that stood out to me was the SynchronizedCollection. I'd never heard of that one before so went searching and found it's basically a generic list with a locking wrapper around each item. if your application gets tons of requests, that collection may not scale quite as well as some new 4.X options but I'm sure it will do the job just fine. thank you for posting the excellent example! :)
 
To be honest, I don't know that I ever need to use a Synchronized Collection given the way I wrote everything. I'm not really enumerating clients at any point, so a simple List should be safe enough across threads. I would need to stop exposing the collection so that later on I didn't accidentally modify it for some reason (honestly, I should do that anyway as I should have no reason to directly modify that list, ever).

Edit: Actually, looks like I DIDN'T expose the list, so yay! I thought about that!

Anyway, I wrote a quick test program to make sure no blocking was occurring, and it does seem to be running as expected when I run it from multiple terminals. The very first message goes through fine, the second (message 1) hangs (so long as I don't let it hang for more than a minute, my timeout in POS Config) until the 8th message is hit. Then both message 1 and message 8 are displayed at the same time.
 
as always, very generous of you to post your hard work here for everyone to learn from!
 
To be honest, I'd probably not have had my oh duh moment if you hadn't mentioned async/await, so thank you, lol.

Edit: Removed the PInvokes code as it isn't really relevant to this forum, and having another giant block of code distracts from what I was originally talking about.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top