The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Net::Elexol::EtherIO24 - Threaded object interface for manipulating Elexol Ether I/O 24 units with Perl

VERSION

Version 0.22.

Requires Perl 5.8.0.

SYNOPSIS

  use Net::Elexol::EtherIO24;

  Net::Elexol::EtherIO24->debug(1);
  my $eio = Net::Elexol::EtherIO24->new(target_addr=>$addr, threaded=>1);

  for my $line (0..23) {
    print "line $line dir: ".$eio->get_line_dir($line)."  ".
          "line $line val: ".$eio->get_line($line)."\n";
  }

  $eio->close;

DESCRIPTION

The Ether I/O 24 manufactured by Elexol is an inexpensive and simple to use and operate device designed for remote control or remote sensing. It has 24 digital lines that are each programmable for input or output and a variety of other things.

The control protocol is relatively simplistic and UDP based. This Perl module attempts to abstract this protocol and add other features along the way. In particular, programmers are encouraged to investigate setting direct_writes => 0 and direct_reads => 0 in the constructor for network efficiency (since these are not yet the defaults).

It is thread savvy and will use threads unless told not to. It might perform adequately without threads, but various functionality would be reduced as a result. In particular, the module functions in a nice asynchronous way when it can use threads. Threads support requires Perl 5.8. This module may not function correctly, or even compile, with an older Perl. Your Perl will require Threads to be enabled at compile-time, even if you don't use Threads with this module.

It uses IO::Socket::INET for network I/O and Time::HiRes for timing. It was developed using Perl on a FreeBSD and a Linux system, but has been known to function using Perl with Cygwin or ActivePerl on Windows.

CONSTRUCTOR

new(args, ...)

Creates a new Net::Elexol::EtherIO24 object complete with associated socket and any necessary threads (if enabled). Returns undef if this is not possible, whereupon the application can check Net::Elexol::EtherIO24->error() for any relevant error string.

Arguments are given in key => value form from these candidates:

target_addr

Address or hostname of the device to communicate with. Mandatory.

target_port

UDP port number to communicate on. Defaults to '2424'.

prefetch_status

Indicates that the current status and configuration of ports on the device should be immediately fetched. Defaults to '1', which enables this feature.

presend_status

Indicates that the initial state should be immediately sent to the device. This would set all lines to inputs with no pullups and threshold set to TTL levels. If at some point we add a way to pre-set this status, then this feature might become useful!

threaded

Enables Perl ithreads and creates a thread to listen to replies from the EtherIO24 unit. This currently requires Perl 5.8 to function. For the most part, client applications do not need to be thread aware, but some functionality may change this assumption. Defaults to '1'.

recv_timeout, service_recv_timeout, service_status_fetch

These values control various timers and are unlikely to need any tweaking. However: recv_timeout is the time recv_result will hang around waiting for an answer. service_recv_timeout is the timer used in the packet receiver thread, when threading, to wait for packets before seeing if there's anything else to do. service_status_fetch is how often in that same thread we fire off a call to the status_fetch method, just to keep our status up-to-date, just in case. These timers are all integer seconds.

recv_timeout defaults to '1.0', service_recv_timeout to '1.0' and service_status_fetch to '60.0'.

direct_writes, direct_reads

These values, which default to '1' (on) control whether the various line_ methods directly query/update the Elexol device or whether they cache data and send/fetch the data to/fom the Elexol device periodically.

The latter method (a setting of '0') can cause less network traffic if you are constantly polling the device at the expense of a marginally longer interval before the device is polled. However, you must call the indirect_write_send method in order to push out writes quickly, or especially if you are not using threads.

By default, if the close method is called any pending writes are sent (See flush_writes_at_close below).

If data is received that would overwrite a pending write then any pending writes are sent.

indirect_write_interval

The interval, in seconds, between background writes to the Elexol device. When not using direct_writes this is the interval at which updates are sent. Defaults to '0.1' (200ms).

indirect_read_interval

The interval, in fractional seconds, after which a cached read value from the Elexol device is considered invalid and must be refetched if that line group is queried. Defaults to '0.5' (500ms).

read_before_write

Defaults to '0'. Forces any "write" functions to "read" the current status first. However, if indirect_reads is '0', it will used the cached value if it has not yet expired.

It should be noted that this is very risky since collisions will occur if two such agents attempt to write to the same group of lines at approximately the same time.

debug, debug_prefix

Controls debugging output. Default value of $debug is inherited from the parent object (if you set it with Net::Elexol::EtherIO24::debug(1) before cloning it).

$debug_prefix is displayed at the start of all debug output and defaults to 'eio24'. You can set this, for example, if you have more than one EtherIO24 object so you can differentiate the debugging output of each.

See also the debug method. Also note that when using threads, the thread ID that produced the debugging output is included after the prefix.

flush_writes_at_close

Defaults to '1', on. Determines whether the indirect_write_send method is called at close to flush any pending writes.

eeprom_read_retries

Number of attempts to read an eeprom location, if it times out. Defaults to '2'.

async_status_sub

By default this is not defined. The developer can pass in a reference to a subroutine that will be called after new status information is received from the Elexol device.

Such new status includes both the response to a status query, or unsolicited updates from the autoscan feature.

The subroutine is passed four parameters:

        $fn($data, $key, $new_value, $old_value)

$data is the $data hash used to store information by the object, and which can optionally be passed in at object creation time (see below).

$key is the index into $data that was updated with new status information. This will be in the form "TYPE GROUP" where TYPE is one of "status", "dir", "pullup", "thresh" and GROUP is one of "A", "B", or "C".

$new_value is the value just received.

$old_value is the previous value.

wakeup

If true then we will attempt to wakeup the module at initialisation. See the 'wakeup' method for details.

data

Various state information is contained within a hash. If not given, one is created and used anonymously. However, the application can pass in a reference to a hash here, for instance to identify this object to an async callback subroutine.

Many of the items in this hash are threads::shared when we are threading.

Developers should prefix their own elements in this hash with a '_' character to ensure uniqueness from those added by this module.

METHODS

close

Closes network resources and waits (briefly) for any running threads to end. Should be called when the host application is ending or when the object is no longer needed.

The object destructor will attempt to call this function when the world ends, but Perl might not be patient enough to wait for threads to end by that time.

wakeup

Send a handful of UDP packets to the device to 'wake it up'. The intention is to trigger MAC address resolution before we send any real packets to it.

This method creates and closes its own socket so it does not interfere with other threads.

It is called at initialisation if you pass 'wakeup' into the constructor.

debug($level)

The higher $level is, the more debugging is output. "3" is currently the useful limit, though "4" will enable hex-dumps of all data sent and received.

Can be called on the parent to set default debugging level, and on each object to control that objects debug level.

error

Returns a string description of the last error, or 0 if no error recorded.

Note that this value is global - it is shared between all EtherIO24 objects so that you can return an error should new() fail to construct a new object.

dump_packet($packet, $offset, $increment)

Returns a string containing a HEX and ASCII dump of the packet in $packet.

This is used in the send/receive routines if a high enough debug level is set and is provided here in case someone else finds it useful.

$offset is optional and specifies the offset in the packet to start at, defaults to 0.

$increment is optional and specifies how many items to display per line, defaults to 16.

clear_cache

Resets the timestamps on all cached data forcing the next read to query the Elexol device.

eeprom_fetch($recv=1, $fetchall=0)

Fetch the contents of the eeprom. If recv is 1 then it will wait for the results to arrive, otherwise it returns immediately.

Additionally gets all the reserved and user space words if $fetchall is 1.

Returns 1 on success, 0 on failure.

status_fetch($recv)

Query the EtherIO24 for its current status. This will store the current status of all I/O lines and their programmed settings. If you don't intend to reset these settings then this is important in order for the Net::Elexol::EtherIO24 to be able to manipulate I/O lines on a per-bit basis.

If recv is 1 then it will wait for the results to arrive, otherwise it returns immediately.

Returns 1 on success, 0 on failure.

status_send

Send all current status to the EtherIO24.

Returns 1 on success, 0 on failure.

indirect_write_send()

This method performs various background tasks such as sending any updates to the Elexol device that are pending and retrieving status from the device.

It should be called periodically (often) if you are not using threads; otherwise it is not necessary (but not harmful) to call this.

verify_send_command($cmd)

Not normally called directly.

Verify commands to be sent. Returns 1 if the command(s) is(are) valid. Returns 0 if any command is invalid. Will search the entire string given for multiple commands.

Will perform various processing tasks on the command, such as resetting the "status received" flags for any status fields referenced by the command.

verify_recv_command($cmd)

Not normally called directly.

Verify a command is valid and if so, return how many bytes of it form that valid command or 0 if invalid.

Will perform various tasks on the command, such as updating the stored status of things referenced in the command. When threading, it also sends a signal to indicate that new status has arrived, if relevant.

send_command($cmd)

Send a packet to the EtherIO24 unit. Passes it through verify_send_command and then to send_pkt.

Returns 0 on failure, or the result of send_pkt otherwise.

recv_command

Not normally called directly. See recv_result() instead.

Wait for a packet from the EtherIO24 unit. Returns an array of received commands upto any point where an invalid command was found in the input. Is NOT thread-friendly unless used in a particular way!

Received packets are passed through verify_recv_command to parse into commands and perform any automatic processing on them.

recv_result($cmd)

Wait for results to arrive. May happen sync or async. Is thread-friendly.

Need to give $cmd for threaded operation so it knows what result in particular to wait for. Returns undef if there was a problem doing this, such as a timeout waiting for the reply.

Replies with the reply command as received, except when it's an eeprom command and threads are being used. In this case the actual command returned is an indeterminate recent eeprom related reply.

read_eeprom($index, [$index, ...])

Reads the given eeprom locations. Always waits for the answer.

Returns the count of locations sucessfuly read or 0 on error.

write_eeprom($index, [$index, ...])

Write the contents of our local eeprom cache for the given index(es) to the Elexol device.

It includes a 100ms delay after each write in order to let the eeprom settle.

eeprom_write_enable($enable)

Enables or disables the "write" flag for the eeprom on the Elexol device.

send_pkt

Send a packet over the socket. Not normally called directly.

recv_pkt

Wait for a packet to come in. Not normally called directly.

reboot

Restarts the module. Needed to make any eeprom changes take affect.

set_line($line, $val)

Sets the line to boolean val and sends to EtherIO module.

Ignored if line is (believed to be) an input.

get_line_live($line)

Returns live boolean value of line.

get_line($line)

Returns the value of the specified I/O line.

If using direct_reads then this method always queries the device. Otherwise this method uses the cached value, unless expired (See indirect_read_interval constructor parameter) whereupon it will query the device.

set_line_dir($line, $dir)

Set line direction. 0 = output, 1 = input.

get_line_dir($line)

Returns direction setting for $line. 0 = output, 1 = input.

See get_line for direct_reads and cachine heuristics.

set_line_pullup($line, $pullup)

Set input line pullup. 0 = pullup off, 1 = pullup on.

get_line_pullup($line)

Returns pullup setting for $line. 0 = pullup off, 1 = pullup on.

See get_line for direct_reads and cachine heuristics.

set_line_thresh($line, $thresh)

Set line threshhold. 0 = 2.5v (TTL), 1 = 1.4v (CMOS).

get_line_thresh($line)

Returns threshold setting for $line. 0 = 2.5v (TTL), 1 = 1.4v (CMOS).

See get_line for direct_reads and cachine heuristics.

set_line_schmitt($line, $schmitt)

Set line Schmitt trigger. 0 = off, 1 = on.

get_line_schmitt($line)

Returns schmitt setting for $line. 0 = off, 1 = on.

See get_line for direct_reads and cachine heuristics.

set_autoscan_addr($addr, $port)

Programs an IP address to use for autoscan functions.

$addr is an ASCII string representation of an IP address. $port is a numeric UDP port number.

If not specified, it will attempt to determine the current IP address and port of the open UDP socket. This may not be wholly portable and your mileage may vary. Unix-like platforms should fare best.

If $addr is a numeric 0 then the autoscan function will be disabled on the module.

Changes made by this function require a module restart to take effect.

Before making changes to the eeprom, this method always reads in the current value first.

set_autoscan_lines($line = $state, ...)>

Sets the autoscan state of the given lines to the given state.

Where $state = 1, the module will send status changes for $line.

Changes made by this function require a module restart to take effect.

set_startup_status($state)

If $state is 1, uses the current status to set the startup status. Details such as line direction, trigger levels, etc are programmed into the eeprom. Status from the module is fetched prior to setting the startup status if not pre-fetched at object creation.

Otherwise, disables startup port status setting.

Changes made by this function require a module restart to take effect.

NOTE

The author is not in any way affiliated with Elexol, the manufacturer of the device this Perl module is designed to operate. This module has been developed using only data available in the public domain.

SEE ALSO

http://www.flirble.org/chrisy/elexol/, http://www.elexol.com/, http://www.elexol.com/Downloads/EtherIO24UM11.pdf

AUTHOR

Chris Luke <chrisy@flirble.org>

BUGS

Please report any bugs or feature requests to bug-net-elexol-etherio24@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-Elexol-EtherIO24. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

ACKNOWLEDGEMENTS

COPYRIGHT & LICENSE

Copyright 2005..2008 Chris Luke, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.