Jump to content

Ron

uniGUI Subscriber
  • Posts

    374
  • Joined

  • Last visited

  • Days Won

    31

Everything posted by Ron

  1. Just tried it on Tokyo, and it generates a pure Javascript application, with a simple HTML wrapper only: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <link rel="icon" href="data:;base64,="> <title>TMS Web Project</title> <script type="text/javascript" src="tmstest.js"></script> <style> </style> </head> <body> </body> <script type="text/javascript"> rtl.run(); </script> </html> So it is a JS compiler, able to run on any http server. The interesting question is of course DB connectivity, in addition to the obvious ones of compatibility and stability, not to mention security. Time will tell. If they can solve these issues, portability will be a good sales argument.
  2. Of course, some of my experience was when creating services to run on customers' computers, and then I have to make things 110% stable. When making a Unigui service, you usually control the hardware and network setup, and if it works, it works, and there is no problem. It is a quite different thing to make something run anywhere, because then you will see that it works in 95% of the cases, but to catch the last 5% you may have to reduce the complexity somehow.
  3. What if you set the default value of that field to 0, at the DB level, and drop that validation?
  4. I agree that it is more straightforward to deploy. I have to admit that I do not have much experience running Unigui as a service. But I have run my business app for 3 years now as a DLL, with only minor issues. Trying to run it as a service failed, and I had to give up after much work. So for me, DLL deployment was the only possible solution. Also, creating a 100% stable Delphi service with a DB connection, where the DB server had a nightly restart, was impossible in some cases. I had to drop the DB connection from the service, and rather have it trigger separate apps, to make this work for all customers. Some times it is easier to go around the problem, than spending half your life trying to go through it.
  5. Do you mean how to add a new line to a UniDBMemo by click of a button? You have to work directly on the field: procedure TMainForm.btnAddMemoLineClick(Sender: TObject); begin with myMemoQuery do fieldByName('memofield').AsString:=fieldByName('memofield').AsString + chr(13) + chr(10) + 'some extra text'; end;
  6. On the DB level there is always differentiation between NULL and 0. But the .IsNull function obviously does not work that way. What if you try, If length(TableName.FieldByName('AmountFrom').AsString)=0 then ShowMessage ("You must enter an amount greater than or equal to 0") Then you only check for NULL (no value) and not 0.
  7. Using the OnHTTPRequest event you can create any web api on the same port as the Unigui app, and serve other clients. But keeping things simple is usually a good idea, by avoiding unnecessary complexity and rather creating separate projects.
  8. Check for both 0 and NULL in the SQL, if possible.
  9. I tried AWS some three years ago and it worked fine, I seem to remember. There were no ISAPI restrictions. But I got faster response from a VPS in a city close by, so I went with that.
  10. I would avoid using Unigui as a service application, plain and simple. Rather use a webserver and compile it as a DLL. I try to avoid creating service apps in general for Windows, and if I have to do it, I make them as simple as possible, with no db connection and if any db process is necessary, I create external programs that are executed by the service app, to avoid anything messing up the service process.
  11. It is Javascript, and you can load it from a textfile and assign it to a uniHtmlFrame and then pick up the response in the AjaxEvent of the frame. You can also inject the JS code at runtime, using the AddJS function (not tested): uniSession.AddJS('window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;'); uniSession.AddJS('var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){}; '); uniSession.AddJS('pc.createDataChannel(""); '); uniSession.AddJS('pc.createOffer(pc.setLocalDescription.bind(pc), noop);'); uniSession.AddJS('pc.onicecandidate = function(ice){ '); uniSession.AddJS('if (ice && ice.candidate && ice.candidate.candidate) { '); uniSession.AddJS('var myIP = /([0-9]{1,3}(\\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];'); uniSession.AddJS('ajaxRequest(MainForm.mainFrame, ["getLocalIP"], { IP : myIP }); '); uniSession.AddJS('pc.onicecandidate = noop; }}; '); And then in the AjaxEvent: procedure TMainForm.mainFrameAjaxEvent(Sender: TComponent; EventName: string; Params: TUniStrings); begin if eventname='getLocalIP' then begin uniMainModule.localIP:=Params.Values['IP']; end; Farshad: Would it be an idea to integrate this webRTC function into the mainmodule, so we can have the local IP as a client connects?
  12. I pick up the local network IP, to exclude other machines from accessing cash register functions, than the one having the physical connection to the receipt printer/cash drawer. Notice the double backslash in the regex. window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){}; pc.createDataChannel(''); pc.createOffer(pc.setLocalDescription.bind(pc), noop); pc.onicecandidate = function(ice) { if (ice && ice.candidate && ice.candidate.candidate) { var myIP = /([0-9]{1,3}(\\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1]; ajaxRequest(MainForm.mainFrame, ['getLocalIP'], { IP : myIP }); pc.onicecandidate = noop; } };
  13. To access a local printer directly you have to set up and go through a local process, and there are several ways to do that. One is cross-origin resource sharing, CORS, and then the local process runs an http server being triggered by a JS client call. Another simple way is polling, like your local process does a regular db polling to see if there is something to be printed. A third way is to start messing with some addons like flash or java to get direct hardware access. In this case the local process is loaded on demand, in the previous cases it must be running beforehand to serve the request.
  14. You can also check where the request comes from: procedure TServerThread.httpServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); function hostOK(host:string):boolean; begin result:=(host=serverIP) or (host=localIP) or (host='127.0.0.1'); end; begin if hostOK(ARequestInfo.RemoteIP) and (ARequestInfo.CommandType=hcPost) and (length(aRequestInfo.Params.text)>0) then begin //do your stuff here end;
  15. @rasaliad, sure this is not difficult: 1. Download the eggcentric DLL loader, at http://www.eggcentric.com/ISAPILoader.htm 2. Unpack it and rename the loader to your app name, e.g. myapp.dll, and copy it to your local webserver app folder, replacing your existing app DLL. 3. Add a post-build command in your Project Options | Build Events, to copy the compiled DLL to your local webserver app folder and at the same time rename the DLL to have the UPDATE ending, e.g. myapp.update. See attached image. 4. Make sure you compile your project as a DLL. And that is basically it. When you compile your app the DLL will be copied and renamed, and the eggcentric DLL loader will notice the presence of the UPDATE file and unload the running DLL, rename the UPDATE to a RUN extension and load the new DLL into memory using ISAPI functions. Of course this assumes you have a local webserver installed, Apache or IIS, and that you are able to run the app as a DLL initially. But this will give you no more reloading issues or exceptions when compiling and restarting the session. I think Farshad talked about including such an update function in the Hyperserver, so then you will only need the post-build command (assuming you are running the Hyperserver locally).
  16. That is true, and very important. With my dev setup, where I compile to DLL, and have a post-build command that copy the DLL to the right folder and rename it to .UPDATE, which then triggers automatic reload in the browser using the eggcentric solution, is hard to beat. I click Ctrl-F9, and then 3-4 seconds later the browser has reloaded the new app, no need for a single extra click. That is one-click compile and reload, integrated client-server development - beat that! If the Hyperserver provides good debugging and logging capabilities it will be difficult to ever catch up with Unigui, unless you have a huge team of post-doc experts and unlimited resources. Maybe an open-source project will be there in 10 years.
  17. Ron

    Printing directly

    QZ write about their Client-side Technology WCPP Utility (a native app for Windows, Linux, Raspberry Pi & Mac without any dependencies!) Simple, small and easy one-time install! So there is a native app, and I am thinking that then you can just as well set up your own service app, running an indy http server and trigger it by jquery cross-origin http calls running in the browser. Maybe you need more than just printing? In a local service app you can arrange that and either reply directly to the CORS call or, if delayed, save the state to the db and do a server-side polling.
  18. I guess it will boil down to stability and debugging abilities, and the question is if it is easier to debug a set of html/css/js files or one DLL. The DLL is basically a black box you can do nothing abou. Trying to comb through html/css/js will be a challenge, but at least you have something to check and eventually hack if there is an issue. If Unigui can offer both outputs, it will have the upper hand.
  19. You can do cross-domain scripting by using JQuery's http get, if you have a local webserver running with header setup for allowing cross-domain calls: UniSession.AddJS('$.get("http://127.0.0.1/test.php?event='+inttostr(EventID)+'", function( data ){' + ' ajaxRequest(MainForm.form, ["testEvent"], { response : data }); '+ ' }); '); I had to use this solution to avoid router NAT setup, and use a local apache httpd with php to catch the call, and then execute a local exe file to check the cash drawer status via a DLL routine. It works great. Why not go through the browser as you are then behind firewalls and routers etc. It seems like the perfect solution. I also have a delphi service that can serve it, but it seems like separating the http server with the other parts makes it more stable when it comes to drops in db connection due to network errors. To allow cross-domain access for indy httpd add a custom header: AResponseInfo.CustomHeaders.AddValue('Access-Control-Allow-Origin', '*'); To allow cross-domain access for apache use a virtualhost directive with a header command: <VirtualHost *:80> DocumentRoot "c:\dev\apache22\htdocs" Header set Access-Control-Allow-Origin "*" </VirtualHost> Of course you should restrict these to a specific domain for security reasons. Without the header the server will get the command, but the browser will cut the response. But how to solve automatic login and/or user change by RFID? Catch a running session...
  20. I have realized this possibility too, but also concluded I could then use any webserver, and since HTTPCommand apparently does not work in DLL-mode it is not realistic for production anyway.
  21. There are in reality only two professional deployment options, service and ISAPI. You have to run on different ports with a service, if you need several apps on a single IP, but with URL rewrite you can mask much if not all of this I guess, or you can use a simple redirect to get clean starting URLs. A URL rewrite would need a separate webserver running doing the rewrite, like IIS or Apache, or whatever works. The crucial difference is that you can kill individual service apps, but with ISAPI you have to kill them all at once as you take the webserver down - and you have to do that if one of the apps hangs or does not allow new connections. The security model is also different, as the webserver handles it for all apps in one go. Automatic updating works well for ISAPI, using the eggcentric solution, and for a service it is a simple case to stop it and update the exe and then restart it - but a little bit more complex it will be, than when using ISAPI, as that involves just a file copy. The more apps you run on a single box, the greater the hassle when having to restart the ISAPI server due to some issue, and the more flexible you are with a service setup when it comes to rebooting individual processes.
  22. Hi, Attached is a simple Unigui test project using websockets with a socket.io server running on a node.js server, and a pure JS websocket client running in a UniHTMLFrame. This is based on the example at https://socket.io/get-started/chat/ and actually worked right out of the box, only had to add the connection url http://localhost:3000/for the client and a few CSS mods. I will try to use this for my calendar project, for sync between logged on clients. I was able to use redis pub/sub for sync of external processes like cash register, but not for calendar sync, as that must be initiated from the browser client, and redis clients cannot do http calls. websocket-unigui.zip
  23. To get the public IP I call a huge php file on my own server. It was years in the making, extremely optimized, especially the return line. <?php echo $_SERVER['REMOTE_ADDR']; return 200; ?>
  24. I noticed there were some "/Handleevent" or whatever PATH_INFO coming in when it ran as localhost, but silent at HTTPCommand when running under ISAPI. It made me wonder - is the framework sending requests to itself through this interface, and what if that is blocked under ISAPI? Are we missing something?
×
×
  • Create New...