--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HostFirmwareUpdater.py Wed Jan 25 11:00:27 2017 +0100
@@ -0,0 +1,154 @@
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +#Copyright (C) 2015: Yvan ROCH +#See COPYING file for copyrights details. +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#------------------------------------------------------------------------------- +# Firmware Updater for the host (the IDE) +#------------------------------------------------------------------------------- +Class which controls the firmware update on the remote PLC +See also TargetFirmwareUpdater +TARGET_UPDATE_SCRIPT = "/opt/beremiz/update.sh" +WAIT_BEFORE_FEED_SEC = 5 +UPDATE_SCRIPT_TIMEOUT = 600 +class HostFirmwareUpdater(object): + # Create a new HostFirmwareUpdater + def __init__(self, _connector, firmwareImagePath, updateType, chunksSize, reboot, logger): + self._connector = _connector + self.firmwareImagePath = firmwareImagePath + self.updateType = updateType + self.chunksSize = chunksSize + self.logger.write(_("HostFirmwareUpdater init.\n")) + self.threadUpdateScript = None + self.runUpdateScriptStatus = None + self.runUpdateScriptTextError = None + # Reset HostFirmwareUpdater internal variables + self.logger.write(_("HostFirmwareUpdater update.\n")) + self.firmwareImageFile = open(self.firmwareImagePath, "rb") + self.imageData = self.firmwareImageFile.read() + self.firmwareImageFileSize = os.stat(self.firmwareImagePath).st_size + self.logger.write(_("Firmware image %s size: %d\n")%(self.firmwareImagePath, self.firmwareImageFileSize)) + # Note Pyro exceptions are already caught by catcher_func and PyroCatcher in PyroProxyProxy + # Create the necessary files on the target + self._CreateFirmwareImageFileSizeFile() + self._CreateFirmwareImageFileSizeMD5File() + self._CreateFirmwareImageFileMD5File() + # Create and start the thread for the update script + self.threadUpdateScript = threading.Thread(target=self._RunUpdateScript) + self.threadUpdateScript.start() + # Waiting some time for the update script launching + self.logger.write(_("Waiting %s s until the update script is running...\n")%WAIT_BEFORE_FEED_SEC) + sleep(WAIT_BEFORE_FEED_SEC) + # The update script is now running: Feeding the update script with firmware image data + # Wait the end of the script + self.threadUpdateScript.join(timeout = UPDATE_SCRIPT_TIMEOUT) + # Here, the update is successful, reboot? + def _CreateFirmwareImageFileSizeFile(self): + # Note Pyro exceptions are already caught by catcher_func and PyroCatcher in PyroProxyProxy + status, textError = self._connector.CreateFirmwareImageFileSizeFile(self.firmwareImageFileSize) + raise Exception(textError) + def _CreateFirmwareImageFileSizeMD5File(self): + # Note Pyro exceptions are already caught by catcher_func and PyroCatcher in PyroProxyProxy + self.firmwareImageFileMD5Size = hashlib.md5("%s\n"%self.firmwareImageFileSize).hexdigest() + self.logger.write(_("Firmware image size md5: %s\n")%(self.firmwareImageFileMD5Size)) + status, textError = self._connector.CreateFirmwareImageFileSizeMD5File(self.firmwareImageFileMD5Size) + raise Exception(textError) + def _CreateFirmwareImageFileMD5File(self): + # Note Pyro exceptions are already caught by catcher_func and PyroCatcher in PyroProxyProxy + self.firmwareImageFileMD5 = hashlib.md5(self.imageData).hexdigest() + self.logger.write(_("Firmware image %s md5: %s\n")%(self.firmwareImagePath, self.firmwareImageFileMD5)) + status, textError = self._connector.CreateFirmwareImageFileMD5File(self.firmwareImageFileMD5) + raise Exception(textError) + def _RunUpdateScript(self): + # Put the update script run status in class attributes because, + # since we are in a child thread, exceptions thrown in this one are painful to catch. + # The update script run status will be checked on joining this thread. + self.runUpdateScriptStatus, self.runUpdateScriptTextError = self._connector.RunUpdateScript(self.updateType) + def _RunFeedPipe(self): + # Check if a multiple chunks transfer is needed + if self.firmwareImageFileSize <= self.chunksSize : + # Only a single chunk transfer is needed + chunksSize = self.firmwareImageFileSize + self.logger.write(_("Single chunk transfer\n")) + # Multiple chunks transfer is needed + chunksSize = self.chunksSize + self.logger.write(_("Multiple chunks transfer\n")) + # Start the image transfer + while transferedBytes < self.firmwareImageFileSize: + chunkData = self.imageData[transferedBytes:transferedBytes + chunksSize] + self.logger.write(_("Transfer chunk, size: %d\n")%(chunksSize)) + status, textError = self._connector.RunFeedPipe(chunkData, isLastChunk, chunksSize) + transferedBytes = transferedBytes + chunksSize + # Detect if the next chunk is the last chunk + if (self.firmwareImageFileSize - transferedBytes) <= self.chunksSize: + chunksSize = self.firmwareImageFileSize - transferedBytes + # Check the transfer and the update status + if self.runUpdateScriptTextError != "" and not self.runUpdateScriptStatus: + textError = "Update script message: " + self.runUpdateScriptTextError + "\n" + textError + raise Exception(textError) + self.logger.write(_("Firmware update log: \n\n") + textError + "\nFirmware update successful.") + def _RebootTarget(self): + self._connector.RebootTarget()