...
 
Commits (3)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# AWL simulator - source management # AWL simulator - source management
# #
# Copyright 2014-2018 Michael Buesch <m@bues.ch> # Copyright 2014-2019 Michael Buesch <m@bues.ch>
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -56,6 +56,7 @@ class SourceFactory(XmlFactory): ...@@ -56,6 +56,7 @@ class SourceFactory(XmlFactory):
name = tag.getAttr("name", source.SRCTYPE) name = tag.getAttr("name", source.SRCTYPE)
filename = tag.getAttr("file", "") filename = tag.getAttr("file", "")
enabled = tag.getAttrBool("enabled", True) enabled = tag.getAttrBool("enabled", True)
volatile = tag.getAttrBool("volatile", False)
if source.SRCTYPE_ID != srcType: if source.SRCTYPE_ID != srcType:
raise self.Error("SourceFactory: Got unexpected " raise self.Error("SourceFactory: Got unexpected "
"source type %d instead of %d." % ( "source type %d instead of %d." % (
...@@ -67,6 +68,7 @@ class SourceFactory(XmlFactory): ...@@ -67,6 +68,7 @@ class SourceFactory(XmlFactory):
source.filepath = "" source.filepath = ""
source.name = name source.name = name
source.enabled = enabled source.enabled = enabled
source.volatile = volatile
self.__haveSourceTag = True self.__haveSourceTag = True
def parser_beginTag(self, tag): def parser_beginTag(self, tag):
...@@ -141,6 +143,7 @@ class SourceFactory(XmlFactory): ...@@ -141,6 +143,7 @@ class SourceFactory(XmlFactory):
"file" : str(filename), "file" : str(filename),
"name" : str(source.name), "name" : str(source.name),
"enabled" : "1" if source.enabled else "0", "enabled" : "1" if source.enabled else "0",
"volatile" : "1" if source.volatile else "",
}, },
data=data, data=data,
tags=childTags, tags=childTags,
...@@ -164,7 +167,8 @@ class GenericSource(object): ...@@ -164,7 +167,8 @@ class GenericSource(object):
enabled=True, enabled=True,
filepath="", filepath="",
sourceBytes=b"", sourceBytes=b"",
userData={}): userData={},
volatile=False):
"""Initialize a source code object. """Initialize a source code object.
name: Name string of the source. name: Name string of the source.
enabled: True, if the source is active. enabled: True, if the source is active.
...@@ -174,12 +178,16 @@ class GenericSource(object): ...@@ -174,12 +178,16 @@ class GenericSource(object):
The source code object will not touch this data. The source code object will not touch this data.
This data will _not_ be transferred to the core server. This data will _not_ be transferred to the core server.
This data does _not_ contribute to the identHash. This data does _not_ contribute to the identHash.
volatile: Optional flag: This source is volatile.
Such sources will not be stored in the core server project.
This flag does _not_ contribute to the identHash.
""" """
self.name = name self.name = name
self.enabled = enabled self.enabled = enabled
self.filepath = filepath self.filepath = filepath
self.sourceBytes = sourceBytes self.sourceBytes = sourceBytes
self.userData = userData.copy() self.userData = userData.copy()
self.volatile = volatile
self.__identHash = None self.__identHash = None
...@@ -201,6 +209,14 @@ class GenericSource(object): ...@@ -201,6 +209,14 @@ class GenericSource(object):
self.__enabled = bool(enabled) self.__enabled = bool(enabled)
self.__identHash = None self.__identHash = None
@property
def volatile(self):
return self.__volatile
@volatile.setter
def volatile(self, volatile):
self.__volatile = bool(volatile)
@property @property
def filepath(self): def filepath(self):
return self.__filepath return self.__filepath
...@@ -358,7 +374,8 @@ class GenericSource(object): ...@@ -358,7 +374,8 @@ class GenericSource(object):
enabled=self.enabled, enabled=self.enabled,
filepath=self.filepath, filepath=self.filepath,
sourceBytes=self.sourceBytes[:], sourceBytes=self.sourceBytes[:],
userData=self.userData) userData=self.userData,
volatile=self.volatile)
def copyFrom(self, other, def copyFrom(self, other,
copyName=True, copyName=True,
...@@ -366,7 +383,8 @@ class GenericSource(object): ...@@ -366,7 +383,8 @@ class GenericSource(object):
copyFilepath=True, copyFilepath=True,
copySourceBytes=True, copySourceBytes=True,
copyUserData=True, copyUserData=True,
updateUserData=False): updateUserData=False,
copyVolatile=True):
"""Copy the content of another source into this one. """Copy the content of another source into this one.
""" """
if copyName: if copyName:
...@@ -381,6 +399,8 @@ class GenericSource(object): ...@@ -381,6 +399,8 @@ class GenericSource(object):
self.userData = other.userData.copy() self.userData = other.userData.copy()
if updateUserData: if updateUserData:
self.userData.update(other.userData) self.userData.update(other.userData)
if copyVolatile:
self.volatile = other.volatile
def __eq__(self, other): def __eq__(self, other):
return self.identHash == other.identHash return self.identHash == other.identHash
...@@ -389,11 +409,12 @@ class GenericSource(object): ...@@ -389,11 +409,12 @@ class GenericSource(object):
return not self.__eq__(other) return not self.__eq__(other)
def __repr__(self): def __repr__(self):
return "%s%s %s%s %s" % ( return "%s%s %s%s%s %s" % (
self.SRCTYPE, self.SRCTYPE,
"" if self.isFileBacked() else " project", "" if self.isFileBacked() else " project",
self.name, self.name,
"" if self.enabled else " (DISABLED)", "" if self.enabled else " (DISABLED)",
" (VOLATILE)" if self.volatile else "",
self.identHashStr) self.identHashStr)
class AwlSource(GenericSource): class AwlSource(GenericSource):
......
...@@ -201,7 +201,7 @@ def safeFileWrite(filename, data): ...@@ -201,7 +201,7 @@ def safeFileWrite(filename, data):
if not osIsPosix: #@nocov if not osIsPosix: #@nocov
# Can't use safe rename on non-POSIX. # Can't use safe rename on non-POSIX.
# Must unlink first. # Must unlink first.
with contextlib.suppress(OSError): with contextlib.suppress(IOError, OSError):
os.unlink(filename) os.unlink(filename)
os.rename(tmpFile, filename) os.rename(tmpFile, filename)
except (IOError, OSError) as e: #@nocov except (IOError, OSError) as e: #@nocov
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# AWL simulator - PLC core server # AWL simulator - PLC core server
# #
# Copyright 2013-2018 Michael Buesch <m@bues.ch> # Copyright 2013-2019 Michael Buesch <m@bues.ch>
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -519,10 +519,16 @@ class AwlSimServer(object): #+cdef ...@@ -519,10 +519,16 @@ class AwlSimServer(object): #+cdef
def __generateProject(self): def __generateProject(self):
cpu = self.__sim.getCPU() cpu = self.__sim.getCPU()
awlSources = self.awlSourceContainer.getSources() awlSources = [ source
fupSources = self.fupSourceContainer.getSources() for source in self.awlSourceContainer.getSources()
if not source.volatile ]
fupSources = [ source
for source in self.fupSourceContainer.getSources()
if not source.volatile ]
kopSources = [] #TODO kopSources = [] #TODO
symTabSources = self.symTabSourceContainer.getSources() symTabSources = [ source
for source in self.symTabSourceContainer.getSources()
if not source.volatile ]
libSelections = self.loadedLibSelections[:] libSelections = self.loadedLibSelections[:]
cpuSpecs = cpu.getSpecs() # (Note: not a deep-copy) cpuSpecs = cpu.getSpecs() # (Note: not a deep-copy)
cpuConf = cpu.getConf() # (Note: not a deep-copy) cpuConf = cpu.getConf() # (Note: not a deep-copy)
...@@ -1145,16 +1151,33 @@ class AwlSimServer(object): #+cdef ...@@ -1145,16 +1151,33 @@ class AwlSimServer(object): #+cdef
if not project: if not project:
return return
if isString(project):
if fileExists(project) == False and writeBack:
# The project file does not exist.
# Create an empty one.
printInfo("Creating empty project at '%s'" %\
project)
empty = Project(project)
empty.toFile()
project = Project.fromProjectOrRawAwlFile(project)
printDebug("Loading project '%s'" % str(project)) printDebug("Loading project '%s'" % str(project))
if isString(project):
projectFile = project
if writeBack:
# If the project file exists and it has zero size
# then delete the file.
if fileExists(projectFile):
with contextlib.suppress(IOError, OSError):
if not os.path.getsize(projectFile):
os.unlink(projectFile)
printInfo("Purged empty project "
"file at '%s'." % projectFile)
if not fileExists(projectFile):
# The project file does not exist.
# Create an empty one.
printInfo("Creating empty project at '%s'" % (
projectFile))
empty = Project(projectFile)
empty.toFile()
# Load the project data.
try:
project = Project.fromFile(projectFile)
except AwlSimError as e:
raise AwlSimError("AwlSimServer: "
"Failed to load project file '%s':\n%s" % (
projectFile, e.message))
self.__resetAll() self.__resetAll()
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# AWL simulator - FUP compiler # AWL simulator - FUP compiler
# #
# Copyright 2016-2018 Michael Buesch <m@bues.ch> # Copyright 2016-2019 Michael Buesch <m@bues.ch>
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -432,7 +432,8 @@ class FupCompiler(object): ...@@ -432,7 +432,8 @@ class FupCompiler(object):
name = "%s (Compiled from FUP/FBD source)" % fupSource.name name = "%s (Compiled from FUP/FBD source)" % fupSource.name
self.awlSource = AwlSource(name=name, self.awlSource = AwlSource(name=name,
enabled=fupSource.enabled, enabled=fupSource.enabled,
filepath=fupSource.filepath) filepath=fupSource.filepath,
volatile=True)
if self.__parse(): if self.__parse():
self.__compileBlockDecl() self.__compileBlockDecl()
insns = self.__compileGrids() insns = self.__compileGrids()
...@@ -489,7 +490,8 @@ class FupCompiler(object): ...@@ -489,7 +490,8 @@ class FupCompiler(object):
awlLines.append("\t)") awlLines.append("\t)")
awlString = "\r\n".join(awlLines) awlString = "\r\n".join(awlLines)
awlSource = AwlSource(name=("CALL " + self.fupSource.name)) awlSource = AwlSource(name=("CALL " + self.fupSource.name),
volatile=True)
awlSource.sourceBytes = awlString.encode(AwlSource.ENCODING) awlSource.sourceBytes = awlString.encode(AwlSource.ENCODING)
return awlSource return awlSource
......