PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/plcfactory.py

https://gitlab.com/gregor_ulm/plc_factory
Python | 416 lines | 324 code | 74 blank | 18 comment | 26 complexity | 9ca97484a9e1941e2a48ea0c8f9ec43b MD5 | raw file
  1. #!/usr/bin/python
  2. """ PLC Factory: Entry point """
  3. __author__ = "Gregor Ulm"
  4. __copyright__ = "Copyright 2016, European Spallation Source, Lund"
  5. __credits__ = [ "Gregor Ulm"
  6. , "David Brodrick"
  7. , "Nick Levchenko"
  8. , "Francois Bellorini"
  9. , "Ricardo Fernandes"
  10. ]
  11. __license__ = "GPLv3"
  12. __maintainer__ = "Gregor Ulm"
  13. __email__ = "gregor.ulm@esss.se"
  14. __status__ = "Production"
  15. __env__ = "Python version 2.7"
  16. # Python libraries
  17. import argparse
  18. import datetime
  19. import os
  20. import sys
  21. import time
  22. # PLC Factory modules
  23. import ccdb
  24. import glob
  25. import plcf
  26. import processTemplate as pt
  27. # global variables
  28. TEMPLATE_DIR = "templates"
  29. OUTPUT_DIR = "output"
  30. def getArtefact(deviceType, filenames, tag, templateID):
  31. assert isinstance(deviceType, str )
  32. assert isinstance(filenames, list)
  33. assert isinstance(tag, str )
  34. assert isinstance(templateID, str )
  35. lines = []
  36. for filename in filenames:
  37. if matchingArtefact(filename, tag, templateID):
  38. ccdb.getArtefact(deviceType, filename)
  39. with open(filename) as f:
  40. lines = f.readlines()
  41. break
  42. return lines
  43. def getTemplateName(deviceType, filenames, templateID):
  44. assert isinstance(deviceType, str )
  45. assert isinstance(filenames, list)
  46. assert isinstance(templateID, str )
  47. result = ""
  48. for filename in filenames:
  49. if matchingArtefact(filename, "TEMPLATE", templateID):
  50. result = filename
  51. # download header and save in template directory
  52. ccdb.getArtefact(deviceType, filename)
  53. break
  54. return result
  55. def matchingArtefact(filename, tag, templateID):
  56. assert isinstance(filename, str)
  57. assert isinstance(tag, str)
  58. assert isinstance(templateID, str)
  59. # attached artefacts may be of different file types, e.g. PDF
  60. if not filename.endswith('.txt'):
  61. return False
  62. # exactly one '.' in filename
  63. assert filename.count('.') == 1
  64. filename = filename.split('.')[0] # removing '.txt.
  65. tmp = filename.split("_") # separating fields in filename
  66. # extract template ID
  67. name = tmp[-1]
  68. return name == templateID and tag in filename
  69. def createFilename(header, device, templateID, deviceType):
  70. assert isinstance(header, list)
  71. assert isinstance(device, str )
  72. assert isinstance(templateID, str )
  73. assert isinstance(deviceType, str )
  74. tag = "#FILENAME"
  75. tagPos = findTag(header, tag)
  76. # default filename is chosen when no custom filename is specified
  77. if len(header) == 0 or tagPos == -1:
  78. outputFile = device + "_" + deviceType + "_template-" + templateID \
  79. + "_" + glob.timestamp + ".scl"
  80. return outputFile
  81. else:
  82. filename = header[tagPos]
  83. # remove tag and strip surrounding whitespace
  84. filename = filename[len(tag):].strip()
  85. filename = plcf.keywordsHeader(filename, device, templateID)
  86. return filename
  87. def findTag(lines, tag):
  88. assert isinstance(lines, list)
  89. assert isinstance(tag, str )
  90. tagPos = -1
  91. for i in range(len(lines)):
  92. if lines[i].startswith(tag):
  93. tagPos = i
  94. break
  95. return tagPos
  96. def processHash(header):
  97. assert isinstance(header, list)
  98. tag = "#HASH"
  99. hashSum = ccdb.getHash()
  100. pos = -1
  101. for i in range(len(header)):
  102. if tag in header[i]:
  103. pos = i
  104. break
  105. if pos == -1:
  106. return header
  107. line = header[pos]
  108. tagPos = line.find(tag)
  109. line = line[:tagPos] + hashSum + line[tagPos + len(tag):]
  110. header[pos] = line
  111. return header
  112. def replaceTag(line, tag, insert):
  113. assert isinstance(line, str)
  114. assert isinstance(tag, str)
  115. assert isinstance(insert, str)
  116. start = line.find(tag)
  117. assert start != -1
  118. end = start + len(tag)
  119. return line[:start] + insert + line[end:]
  120. # ensures that filenames are legal in Windows
  121. # (OSX automatically replaces illegal characters)
  122. def sanitizeFilename(filename):
  123. assert isinstance(filename, str)
  124. result = map(lambda x: '_' if x in '<>:"/\|?*' else x, filename)
  125. return "".join(result)
  126. def processRoot(templateID, device):
  127. assert isinstance(templateID, str)
  128. assert isinstance(device, str)
  129. # get artifact names of files attached to root device
  130. (deviceType, rootArtefacts) = ccdb.getArtefactNames(device)
  131. # find devices this PLC controls
  132. controls = ccdb.control(device)
  133. print device + " controls: "
  134. for elem in controls:
  135. print "\t- " + elem
  136. print "\n"
  137. # change working directory to template directory
  138. os.chdir(TEMPLATE_DIR)
  139. header = getArtefact(deviceType, rootArtefacts, "HEADER", templateID)
  140. if len(header) == 0:
  141. print "No header found.\n"
  142. else:
  143. print "Header read.\n"
  144. footer = getArtefact(deviceType, rootArtefacts, "FOOTER", templateID)
  145. if len(footer) == 0:
  146. print "No footer found.\n"
  147. else:
  148. print "Footer read.\n"
  149. print "Processing entire tree of controls-relationships:\n"
  150. return (deviceType, rootArtefacts, controls, header, footer)
  151. def processTemplateID(templateID, device):
  152. assert isinstance(templateID, str)
  153. assert isinstance(device, str)
  154. print "#" * 60
  155. print "Template ID " + templateID
  156. print "Device at root: " + device + "\n"
  157. # collect lines to be written at the end
  158. output = []
  159. # process header/footer
  160. (deviceType, rootArtefacts, controls, header, footer) = \
  161. processRoot(templateID, device)
  162. # for each device, find corresponding template and process it
  163. output = []
  164. toProcess = controls # starting with devices controlled by PLC
  165. processed = set()
  166. outputFile = \
  167. createFilename(header, device, templateID, deviceType)
  168. headerFileName = ""
  169. headerFiles = filter(lambda x: "HEADER" in x and templateID in x, rootArtefacts)
  170. if len(headerFiles) >= 1:
  171. headerFileName = headerFiles[0]
  172. if not headerFileName == "":
  173. header = pt.process(device, headerFileName)
  174. while toProcess != []:
  175. elem = toProcess.pop()
  176. if elem in processed: # this should be redundant
  177. continue
  178. print elem
  179. # get template
  180. (deviceType, artefacts) = ccdb.getArtefactNames(elem)
  181. print "Device type: " + deviceType
  182. filename = getTemplateName(deviceType, artefacts, templateID)
  183. if filename != "":
  184. # process template and add result to output
  185. output += pt.process(elem, filename)
  186. output.append("\n\n")
  187. print "Template processed."
  188. else:
  189. print "No template found."
  190. controls = ccdb.control(elem)
  191. print "This device controls: "
  192. if len(controls) > 0:
  193. for c in controls:
  194. print "\t- " + c #, c in processed
  195. if c not in processed:
  196. toProcess.append(c)
  197. else:
  198. print "N/A"
  199. print "=" * 40
  200. processed.add(elem)
  201. print "\n"
  202. os.chdir("..")
  203. # process #HASH keyword in header
  204. header = processHash(header)
  205. output = header + output + footer
  206. outputFile = sanitizeFilename(outputFile)
  207. if len(output) == 0:
  208. print "There were no templates for ID = " + templateID + ".\n"
  209. return
  210. lines = output
  211. output = []
  212. # Process counters; initialize
  213. numOfCounters = 9
  214. counters = dict()
  215. for n in range(numOfCounters):
  216. counters["Counter" + str(n + 1)] = 0
  217. for line in lines:
  218. if "[PLCF#" in line and "#COUNTER" not in line:
  219. line = plcf.evalCounter(line, counters)
  220. elif "[PLCF#" in line and '#COUNTER' in line:
  221. (counters, line) = plcf.evalCounterIncrease(line, counters)
  222. assert isinstance(line, str)
  223. assert "[PLCF#" not in line # PLCF should now all be be processed
  224. output.append(line)
  225. #write file
  226. os.chdir(OUTPUT_DIR)
  227. with open(outputFile,'w') as f:
  228. for line in output:
  229. line = line.rstrip()
  230. if not line.startswith("#COUNTER") \
  231. and not line.startswith("#FILENAME"):
  232. f.write(line + "\n")
  233. os.chdir("..")
  234. print "Output file written: " + outputFile + "\n",
  235. print "Hash sum: " + glob.hashSum
  236. if __name__ == "__main__":
  237. os.system('clear')
  238. start_time = time.time()
  239. glob.timestamp = '{:%Y%m%d%H%M%S}'.format(datetime.datetime.now())
  240. parser = argparse.ArgumentParser()
  241. parser.add_argument(
  242. '-d',
  243. '--device',
  244. help='device / installation slot',
  245. required=True
  246. )
  247. parser.add_argument(
  248. '-t',
  249. '--template',
  250. help='template name',
  251. nargs = '*',
  252. type=str,
  253. required=True)
  254. parser.add_argument(
  255. '--test',
  256. help='select test database',
  257. action='store_true',
  258. required=False)
  259. # this argument is just for show as the corresponding value is
  260. # set to True by default
  261. parser.add_argument(
  262. '--production',
  263. help='select production database',
  264. action='store_true',
  265. required=False)
  266. # retrieve parameters
  267. args = parser.parse_args()
  268. # PLC name and template number given as arguments
  269. device = args.device
  270. templateIDs = args.template
  271. if args.test:
  272. glob.production = False
  273. assert len(templateIDs) >= 1, "at least one template ID must be given"
  274. # remove templates downloaded in a previous run
  275. os.chdir(TEMPLATE_DIR)
  276. files = [f for f in os.listdir('.') if os.path.isfile(f)]
  277. for f in files:
  278. if "TEMPLATE" in f:
  279. os.remove(f)
  280. os.chdir("..")
  281. map(lambda x: processTemplateID(x, device), templateIDs)
  282. print("--- %.1f seconds ---\n" % (time.time() - start_time))