lpcmanager

Smarteh 485: uart data is copied to beremiz buffer even if reception was not successfull. This ensures that communication statuses are updated. Otherwise these statuses are not updated in case controller can't communicate with any slave. This bug was introduced in revision 614.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#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
"""
import os
import hashlib
import threading
from time import sleep
import wx
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.Reset()
self._connector = _connector
self.firmwareImagePath = firmwareImagePath
self.updateType = updateType
self.chunksSize = chunksSize
self.reboot = reboot
self.logger = logger
self.logger.write(_("HostFirmwareUpdater init.\n"))
self.threadUpdateScript = None
self.runUpdateScriptStatus = None
self.runUpdateScriptTextError = None
# Reset HostFirmwareUpdater internal variables
def Reset(self):
self.Running = 0
def update(self):
self.logger.write(_("HostFirmwareUpdater update.\n"))
self.firmwareImageFile = open(self.firmwareImagePath, "rb")
# Read the image file
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
self._RunFeedPipe()
# Wait the end of the script
self.threadUpdateScript.join(timeout = UPDATE_SCRIPT_TIMEOUT)
# Here, the update is successful, reboot?
if self.reboot:
self._RebootTarget()
def _CreateFirmwareImageFileSizeFile(self):
# Note Pyro exceptions are already caught by catcher_func and PyroCatcher in PyroProxyProxy
status, textError = self._connector.CreateFirmwareImageFileSizeFile(self.firmwareImageFileSize)
if not status :
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)
if not status :
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)
if not status :
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
isLastChunk = True
chunksSize = self.firmwareImageFileSize
self.logger.write(_("Single chunk transfer\n"))
else:
# Multiple chunks transfer is needed
isLastChunk = False
chunksSize = self.chunksSize
self.logger.write(_("Multiple chunks transfer\n"))
# Start the image transfer
transferedBytes = 0
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)
if not status :
break
transferedBytes = transferedBytes + chunksSize
# Detect if the next chunk is the last chunk
if (self.firmwareImageFileSize - transferedBytes) <= self.chunksSize:
isLastChunk = True
chunksSize = self.firmwareImageFileSize - transferedBytes
# Check the transfer and the update status
if not status :
if self.runUpdateScriptTextError != "" and not self.runUpdateScriptStatus:
textError = "Update script message: " + self.runUpdateScriptTextError + "\n" + textError
raise Exception(textError)
else:
self.logger.write(_("Firmware update log: \n\n") + textError + "\nFirmware update successful.")
def _RebootTarget(self):
self._connector.RebootTarget()