ARexxComm.m v1.1 by Chris S Handley (Chris.S.Handley@BTInternet.com) ====================================== Introduction ------------ This is a fairly easy to use module for AmigaE which allows you to communicate using an ARexxPort. It is infact OO (Object Orientated), but if you don't know about OO don't worry - AmigaE makes OO dead easy. You could probably do ok by just altering the supplied example program. But read the docs anyway, as there are unfortunately still a few subtleties. Although it appears to work well, I should point out that it has not been heavily tested yet. That will only happen when either I (or someone else!) writes a proper program that uses it a lot - when I have time... Preamble -------- ARexxComm.m is heavily based on ARexxPort.e v1.0 by Leon Woestenberg (leon@stack.urc.tue.nl) - this was a great technical source with top comments, but it's style was unreadable, and was totally unsuited to modern programming practices - basically used global vars for communication, plus could not 'drop in' to a program as a procedure required large modifications... Using ARexxPort would have required A LOT of knowledge. I have spent 'quite' a long time completely restructuring much of it, checking logic & some ARexx calls, altering & creating procedures, and so on - basically done lots of work to make it fast, safe, and easy! So, you can finally send/recieve arexx messages as a simple OO AmigaE MODULE. I have not yet tested ARexxComm 'to the limit', so it is possible that some errors from the original source will still appear in exceptional circumstances. Until I have the time to fully read the official docs of ARexx, such small bugs may not be discovered. But if anyone finds such a bug, I will do my best to fix it. History ------- v1.1 (21-03-99) -Fixed crash caused by closing port before it had recieved all acknowledgements. -Change behaviour of createPort() so that you can actually know that a port was not created without using exceptions - you MUST re-read how to use it! Example programs were totally wrong! -Improved docs slightly. v1.01(09-03-99) -Improved docs. -Minor code update. -1st Aminet release, but Aminet is down... v1.0 (02-12-98) -First public release to a few people. Requirements ------------ rexxsyslib.library - standard on WB3.x (maybe 2.x too?) AmigaE v3.x - the full version is now free, so no excuse... Installation ------------ Copy ARexxComm.m to Emodules: or some sub-directory such as Emodules:CSH (CSH being my initials). It is assumed you will create & install it in the CSH dir. The supplied .m file has been generated with the latest registered version of EC, but the source is supplied so you can do this yourself (I believe code should be free!). To use the example programs ARexxComm-S & ARexxComm-R, you will need to compile them first. The ARexxComm-simple program also needs compiling. To see ARexxComm in action, now just run (the compiled) ARexxComm-R (the Reciever), move it's output window elsewhere, then run ARexxComm-S (the Sender) in a different Shell window. The two will then talk to each other! If you want to see the real basics of opening & using a port, look at ARexxComm-simple. That's it! Usage - by example ------------------ See the included example programs for a proper example of most of it's features, but a fairly good description is given below. Example segments of programs are indented. If you find this heavy going, look at the example programs & try modifying that. First we say we will use the module: MODULE 'CSH/arexxcomm' Inside the procedure we must define the object that represents the port (myport), the string pointers used to store any recieved commands & args, and finally the wasack variable who's use will be explained later: DEF myport=NIL:PTR TO rxcom, command=NIL:PTR TO CHAR, args=NIL:PTR TO CHAR, wasack=FALSE Then we can open the port: NEW myport.createPort('myportname',{myport}) Note that 'myportname' is the public name of your new port, and {myport} gives it the address of your myport variable which allows it to set myport to NIL if it could not create the port; use TRUE instead of {myport} if you want an exception raised instead, but most programs may work without an ARexx port. Be SURE to check the port was created: IF myport=NIL THEN ->Port creation failed. Do NOT use other methods. IF myport THEN ->Port creation worked. Do ARexxComm stuff now. Now you may look to see if other public ports (not necessarily ARexx ports) exist using: portinfo.portExists('REXX') This looks for the Rexx Master's port (which is needed to run ARexx scripts). You may not need to use this at all, but it can be very useful. Now we can either send an arexx message or wait to recieve an arexx message. Here is how we send one: myport.sendRexxMsg('targetport','COMMAND') If we wish to add args, you must currently use something like: myport.sendRexxMsg('targetport','COMMAND ARG1 ARG2') Note that 'targetport' is the destination of our message 'COMMAND'. Currently you cannot accurately find-out if your message was recieved - only that one of your messages was recieved (no specific identification); see the "TO DO" section if you want this 'fixed'. Special note! To make Rexx Master run an 'in-line' piece of ARexx code (possibly for running a few AmigaDOS commands from AmigaE), the REXX port expects the entire code as COMMAND. Since such code contains spaces, it should not be possible... But REXX (& possibly other programs) support enclosing the COMMAND or any ARGument in "double quotes" - the only problem is that your ARexx code therefore cannot contain these quotes itself (but remember that the other type of 'quotes' are recognised by ARexx too). To recieve a message: command,args:=myport.waitForRexxMsg() This causes your program to *halt* until a message is recieved. Notice that command is an Estring (pointer) containing the recieved command, and that args is an Estring (pointer) containing the recieved args. If a Ctrl-C is recieved then these string pointers will be set to NIL, so take care! Command & args ALWAYS hold a valid Estring, but may be empty (length 0) if no command or args were recieved. Command contains NO spaces, BUT args may contain any number of words (seperate arguments) which are seperated by spaces, etc.. Args is exactly what an ARexx program recieves in Arg(1) when called from AmigaDos with some paramters - thus you need to do the equivalent of PARSEing in E; this is up to you, sadly. However, perhaps my in-progress CString module may help with this eventually... ADDITIONALLY, you could use an extra feature: command,args,wasack:=myport.waitForRexxMsg(TRUE) As well as doing what it did before, now it may *instead* return when we recieve an acknowledgement of *one* of our sent messages - if it does (wasack=TRUE) then command & args *will* be equal to NIL. This may occasionally be of use. You must read about ackRexxMsg() below. ALTERNATIVELY you can recieve a message like this: command,args:=myport.pollForRexxMsg():=myport.pollForRexxMsg() This will return a command if one has arrived (and any related args), otherwise NIL is returned for both. For interactive programs you would probably do this. IT IS STRONGLY SUGGESTED that you do NOT just repeadly poll for a command from a small loop - this is called "busy waiting" and consumes vast amounts of CPU time, whereas using waitForRexxmsg() uses no CPU time at all! ADDITIONALLY, you can use the wasack feature just as with waitForRexxMsg(TRUE). It is extremely important to note that you MUST do this after getting a command (must acknowledging it): cmdknown=TRUE /*or FALSE*/ myport.ackRexxMsg(cmdknown) This is to tell ARexxComm whether you recognise the command (TRUE) or not (FALSE); if it is recognised then the sender is told it was successfully delivered, but if it was NOT recognised then the message (with the command) is passed on to Rexx Master to deal with (as it may be an ARexx command from an ARexx script). ARexxComm hides the fact that it recieves the 'sent successfully' message from Rexx Master, and that this acknowledgment is sent back to the original sender (as if you had used TRUE). You must do this *once* (but only once) - and it MUST be done before the method waitForRexxMsg() or pollForRexxMsg() is called again! This is the only tricky restriction in using ARexxComm. An exception will be raised if you do something wrong :-) . I may eventually solve this too... Once ackRexxMsg() has been called, you can no longer use the command or args Estring you have acknowledged. Copy that string first if you need to use it later (don't just copy the pointer;-). A little useful method which returns how many acknowledgements have yet to be recieved (processed actually): numofacksleft:=myport.howManyAcksLeft() Finally, you can kill the port when you close down: END myport Usage - of each method ---------------------- There is a precise list of all the methods you can use, with their techy details: -NEW port.createPort(portname,useexceptionsoraddr=TRUE,priority=0) portname = name of your port, eg. 'MYPORT'. useexceptionsoraddr = TAKE CARE AS THIS HAS TWO USES! If createPort() could not open the port then a TRUE value causes it to raise an exception (see below) - HOWEVER, any other value is taken as the address given by {port} so that it may set your port variable to NIL despite you not having used END. Non-library/port exceptions will always be raised! priority = public port priority; use default unless you know otherwise. RETURNS nothing - no point since this is always used with NEW & you see what NEW returns, not what this method returns! USE once at the beginning of your program (for each port you want to create). -port.portExists(portname) portname = name of port to look for, eg. 'REXX'. RETURNS TRUE if it exists, else returns FALSE. USE whenever wish to see if a port exists; could use to decide why sendRexxMsg() failed. NOTE that if you wish to use this ability BEFORE creating an ARexxComm object, use "FindPort(portname)" instead, which returns same values. -port.sendRexxMsg(hostname:PTR TO CHAR, command:PTR TO CHAR, flags=0) hostname = name of the port to send the command to (string). command = the command to send (string); the string may have args after the command (seperate with a space). flags = use default unless you know otherwise. RETURNS NIL if could not send message, otherwise returns some other value (actually the value returned by CreateRexxMsg - i.e.rexxmsg). USE to send a message from your program to some other program. -port.waitForRexxMsg(tellofack=FALSE) RETURNS 'command,args,wasack'; command is an Estring holding the recieved command, BUT is set to NIL if a Ctrl-C was recieved (*CAREFUL!*). Args is an Estring for any recieved args, but may be empty if none were supplied. Again it is NIL if a Ctrl-C is recieved... The args are expected as part of the command string (after the command). If tellofack=TRUE then it may return with wasack=TRUE (as well as command & args equal NIL), which indicates one of our sent messages was acknowledged. USE to wait for a valid command which you must then act on *before* using ackRexxMsg(). NOTE that after calling this proc., you MUST use ackRexxMsg() BEFORE you call this proc. again - fail to do this and an exception will be raised. -port.pollForRexxMsg(tellofack=FALSE) RETURNS 'command,args,wasacks'; command is an Estring holding the recieved command, BUT is set to NIL if there was no command yet. The same is true with args, but may be empty if no args were receieved. The args are expected as part of the command string (after the command). Both tellofack & wasack behaves as for waitForRexxMsg(). USE to look for a valid command which you must then act on *before* using ackRexxMsg(). NOTE that after calling this proc., IF a command was recieved then you MUST use ackRexxMsg() BEFORE you call this proc. again - fail to do this and an exception will be raised. -port.ackRexxMsg(commandknown) commandknown = depends on the command returned by waitForRexxMsg(); if the command WAS recognised then use TRUE, else use FALSE (FALSE causes ARexxComm to send the command to Rexx Master, as may have been a valid Rexx instruction from a script) RETURNS nothing. USE for dealing with the command returned by waitForRexxMsg() to acknowledge that the command has been handled; do *after* as this proc. causes the command string to be removed (i.e.command pointer becomes invalid). Copy the string if you need a more permanent record. NOTE that an exception will be raised there is no message to reply to, or if the last message was already replied to. NEVER refer to the command string again since it is now invalid! -port.howManyAcksLeft() RETURNS number of acknowledgements yet to be recieved. Notice that this is *not* updated until waitForRexxMsg() or pollForRexxMsg() are called, since only then is the message queue examined (and any acknowledgements noted). In fact, unless you call waitForRexxMsg() with tellofack=FALSE, the number left can only decrease by 0 or 1. USE easy way for user to know who well recieved his messages are! -END port RETURNS nothing. USE once at the end of your program to remove your port & deallocate resources. NOTE that this can NOT be called more than once without using the NILCHECK switch on EC when compiling. Usage - of exceptions --------------------- Here is a list of exceptions that ARexxComm will specifically raise: exception exceptioninfo Reason "LIB" (library name) Could not open library (can be disabled). "PORT" (port name) Could not open ARexx port (can be disabled). "RXNR" (rexx command) Did not acknowledge previous ARexx message. "RXNM" - No ARexx message to acknowledge. "RXCM" (description) Method object-use error - details supplied. (most likely didn't use createPort() first) Usage - Hints ------------- -When sending or recieving ARexx messages, note that commands & arguments do not usually contain spaces, but that some programs such as Rexx Master (REXX) do allow this by enclosing such items in "double quotes". As REXX expects any command (such as in-line ARexx code) to be all inside the command part, it is necessary to enclose the entire 'command' in those "quotes". -Remember that any sent commands occure IN PARALLEL with your AmigaE code, due to the Amiga's multitasking nature. To wait for the command to be finished, you will need to wait for an acknowledgement - if you have sent more than one command, then currently you cannot tell which message has been acknowledged. This could be fixed, but would require some effort on my part. Thanks to (in rough chronological order, most recent first) --------- Jody Tierney - For supplying the technical description of ARexx ports, even if I haven't had time to read it yet... Maxim Olivier-Adlhoch - For finding how to cleanly avoid a possible (but rare) machine freeze. Also for some detailed help on general aspects of ARexx ports. Mauro Fontana - For also helping on many aspects of ARexx, even if I couldn't get his suggestion on fixing the freezing problem to work! The E mailing list - Everyone else who helped me - wow, what a bunch of really nice & helpful people :-) Leon Woestenberg - Without his source ARexxComm would not exist! Wouter van Oortmerssen - For such a beautiful language ^_^ . Please make it portable... Bugs ---- Known bugs: -Pressing Ctrl-C does not always work while using waitForRexxMsg(), but it seems that this is an OS or library "feature", where a waiting Rexx message takes prescedence over a Ctrl-C. I may be wrong about the reason... If you find a bug, ONLY report it if: (a)you can *tell me EXACTLY how to reproduce it* - I would prefer the source code of an E program that causes the bug to happen. (b)the program told you to report it! Along with telling me how to reproduce it, please tell me what your hardware & software set-up is, if chip/fast memory was low, etc.. And try to be sure the bug is not caused by some nasty patch to the OS - I patch my miggy alot, so I know that the majority of patches do cause problems! To do ----- 1.As well as fixing bugs, I have a list of several features to add to ARexxComm, but telling me if you want something may well make it get implemented quicker: -Fix reported bugs! 2.These COULD be done (and quite easily) but won't be unless someone asks specifically for it to be implemented (with a good reason why): -Make Ctrl-C sensing optional. -Send/recieve args stored seperatly from command string (in rexxargs[1+]). 3.These are possible, but would take quite a bit of work: -Ability to recieve several messages before acknowledging them. This also allows knowing which messages were acknowledged. Might not be that hard actually, but I need a reason to look into it. Bribery (such a with a 60ns 32Mb SIMM;) is not necessary, BUT could well make any requests a higher priority... (hell, I'd sell my soul for that SIMM:-) Latest notes ------------ I am currently working on CString (a all-in-one EString manipulation class to make strings nearly as easy as in BASIC!) & SmartArrayList (a very flexible way of storing data that can be accessed as a 4billion entry array & list at the same times, with decent speed & no hassle allocating memory). My MSc course is taking ridiculous amounts of time, so progress in these is slow (but kinda stready). When finished, I will start work on recoding GetAllHTML.rexx into AmigaE - this will fairly heavily test ARexxComm, and provide motivation to improve it drastically; being honest, something is much more likely to get fixed if I need it fixed (or someone sends me fixed code or a 32Mb 60ns SIMM). Additionally, I will also start coding a novel new language I am designing (which is accidentally closest to Lisp, but more elegant IMHO), and perhaps even some experimental tries at both an AI program & also an automated web-news gatherer (the latter learning your preferences). Rather a lot to do. Ho hum :) Oh, and I haven't been best impressed with AF's treatment of my letters (they seem to get rather upset at a little criticism from one person), but as long as no one gets the wrong impression of me from their edited version of my letters ("in full" I don't think), I'd rather lay my rather unimportant criticisms to rest. Rants on this subject are NOT welcome, but short & level-headed emails are :-) . If I am wrong, I whole-heartedly wish to know it... I now own a BVision gfx card. Cooooooool! Except I get some crashes now The Author ---------- My name is Christopher S Handley, aged 21, my (permanent) email address is Chris.S.Handley@BTInternet.com. I have got a 2.1 BEng(Hons) in Electronic Engineering (specialising in digital system & chip design) after 3 years at Sheffield University, and am currently doing a 1 year MSc in the same area. I'm also looking for possible jobs - doubt I'll get any offers cos of ARexxComm though! In no particular order my interests include Science Fiction (mostly hard SF books, esp.robot stories), Japanese Anime (ones with believable characters & decent plots, or at least very funny, and definitely *not* the violent "Manga" type of Anime videos), the Amiga since Xmas 1990 (even after using Macs & PCs a lot, I find it the nicest computer to use), programming (mostly in AmigaE, ARexx, AmigaDOS, and occasionally C & VHDL), music (I couldn't live without it!), generally thinking up neat theories & cool algorithms, learning Japanese (slowly), cunning things you can do in chip design, reading 'true' Manga (Japanese comics), cross country running (not that I've done much lately), and thats probably enough for now! Final notes: ------------ 1.The ARexxPort source was completely in the Public Domain, and so is ARexxComm, BUT on the condition that anything made using the source is NOT called ARexxComm, and that both me (Chris S Handley) & Leon Woestenberg are given full credit. If you wish some feature to be included in ARexxComm, either ask me nicely to write the feature in, or give me well documented code changes; if I have the time & think the changes are a good idea, then they may well get done. It would be nice to keep ARexxComm being kept as one coherent release, unlike what happens in the Unix world. 2.For those with a technical interest... If you tell ackRexxMsg() that you didn't recognise the command, then a NEW message is created that is sent to Rexx Master. This new message contains a pointer to the first message. Now, when the new message is acknowledged by Rexx Master, we can remove the first message from memory (by using the pointer). In this way, ARexx scripts can transparantly be used with ARexx commands. 3.It'd be nice if you mentioned that ARexxComm was used in any of your programs, to me, as well as in the docs :-). 4.If I cannot be reached by my BTInternet email address, in an emergency I may be reached at Chris.S.Handley@eva01.freeserve.co.uk but I usually only check this once a month at best!