I have ordered a USB to RS232 cable (USB-RS232 WEUSB-RS232 WE) to be able to communicate to the AB SLC PLC
I have ordered a USB to RS422 cable (USB-RS422 WEUSB-RS422 WE) to be able to communicate to the Fenner M-Trim controller
FTP Communication
While waiting for the above cables to arrive, I have been working to get the FTP communications working
OEE data
Data will be read from the PLC every minute and stored to a local file. The local file will be sent to the server every hour.
Recipe data
Recipe data will be stored on the same server. Each recipe will have it's own file on the server. A list of those files will be stored in a separate file with a specific name. The plan is to retrieve the single file then iterate through the lines of the file, downloading the recipes to local storage on the pi.
The Twisted framework is being used to accomplish the communication in this project.The FTPClient protocol has been adapted to allow the 'APPE' instruction. The LoopingCall is being used to schedule the sending of the OEE data to the server every hour (3600 seconds) and the receiving of the recipe file from the server (every 24 hours).
One drawback that I did find with the Twisted framework was when I manually disconnected the network cable from the pi, it took about 20 minutes for the code to realize the connection was lost. There wasn't any lost data as far as I could see. The data was transferred when the connection was reestablished.
So week 1 was a success in establishing FTP communications. Later in the project I will make the times selectable through the PIFace controller.
class FTPClientA(FTPClient):
""" **************************************************************
Protocol subclass from FTPClient to add 'APPE' support allowing
the ability to append data to a file.
Also using connectionMade method to start data transfer loops
************************************************************** """
def __init__(self, username = 'anonymous', password = 'anonymous@', passive = 1):
FTPClient.__init__(self, username, password, passive)
self.OEELoop = LoopingCall(sendOEEData, self)
self.RecipeDownload.LoopingCall(rerieveRecipes, self)
def connectionMade(self):
""" ****************************************************************
Called when a connection is made.
This may be considered the initializer of the protocol, because
it is called when the connection is completed. For clients,
this is called once the connection to the server has been
established; for servers, this is called after an accept() call
stops blocking and a socket has been received. If you need to
send any greeting or initial message, do it here.
***************************************************************** """
self.OEELoop.start(3600).addErrback(fail)
self.RecipeDownload.start(86400).addErrback(fail)
def connectionLost(self, reason):
""" ****************************************************************
Called when the connection is shut down.
Clear any circular references here and any external references to
this protocol. The connection has been closed.
@type reson: L{twisted.python.failure.Failure}
****************************************************************** """
print "connection lost"
self.OEELoop.stop()
def appendFile(self, path):
""" ******************************************************************
Append to a file at the given path
This method issues the 'APPE' FTP command
@return: A Tuple of two L{Deferred}s:
-L{Deferred} L{IFinishableConsumer}. You must call
the C{finish} method on the IFinishableConsumer when the file
is completely transferred.
-L{Deferred} list of control-connection responses.
****************************************************************** """
cmds = ['APPE ' + self.escapePath(path)]
return self.sendToConnection(cmds)
appe = appendFile
class FTPClientAFactory(ReconnectingClientFactory):
def buildProtocol(self, addr):
self.resetDelay()
p = FTPClientA(username='anonymous', password='anonymous@')
p.factory = self
return p
def clientConnectionLost(self, connector, reason):
""" *************************************************************
Called when a connection has been lost after it was connected.
@type reason: L{twisted.python.failure.Failure}
************************************************************* """
print 'Lost connection. Reason:', reason
ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
def clientConnectionFailed(self, connector, reason):
""" *************************************************************
Called when a connection has failed to connect.
@type reason: L{twisted.python.failure.Failure}
************************************************************* """
print 'Connection failed. Reason:', reason
ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
def run():
# Get config
config = Options()
config.parseOptions()
config.opts['port'] = int(config.opts['port'])
config.opts['passive'] = int(config.opts['passive'])
config.opts['debug'] = int(config.opts['debug'])
# Create the client
connector = reactor.connectTCP(config.opts['host'], config.opts['port'], FTPClientAFactory())
reactor.run()