pyprofibus-linuxcnc-hal 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # PROFIBUS DP - LinuxCNC HAL module
  5. #
  6. # Copyright 2016 Michael Buesch <m@bues.ch>
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License along
  19. # with this program; if not, write to the Free Software Foundation, Inc.,
  20. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. #
  22. from __future__ import division, absolute_import, print_function, unicode_literals
  23. import sys
  24. import os
  25. import time
  26. import getopt
  27. import struct
  28. from pyprofibus import DPM1, DPM2, DpSlaveDesc, isPy2Compat
  29. from pyprofibus import ProfibusError, FdlError, DpError
  30. from pyprofibus.util import fileExists, monotonic_time
  31. from pyprofibus.phy_serial import CpPhySerial
  32. from pyprofibus.gsd.interp import GsdInterp
  33. from pyprofibus.dp import DpTelegram_SetPrm_Req
  34. from pyprofibus.conf import PbConf, PbConfError
  35. from pyprofibus.version import *
  36. class SigBit(object):
  37. def __init__(self, hal, halName, byteOffset, bitOffset):
  38. self.hal = hal
  39. self.halName = halName
  40. self.byteOffset = byteOffset
  41. self.bitOffset = bitOffset
  42. self.setMask = 1 << bitOffset
  43. self.clrMask = ~(1 << bitOffset)
  44. def fromHal(self, destBuf):
  45. if self.hal[self.halName]:
  46. destBuf[self.byteOffset] |= self.setMask
  47. else:
  48. destBuf[self.byteOffset] &= self.clrMask
  49. def toHal(self, srcBuf):
  50. self.hal[self.halName] = (srcBuf[self.byteOffset] >> self.bitOffset) & 1
  51. def __str__(self):
  52. return "profibus.%s" % self.halName
  53. class SigU8(object):
  54. def __init__(self, hal, halName, offset):
  55. self.hal = hal
  56. self.halName = halName
  57. self.offset = offset
  58. def fromHal(self, destBuf):
  59. destBuf[self.offset] = self.hal[self.halName] & 0xFF
  60. def toHal(self, srcBuf):
  61. self.hal[self.halName] = srcBuf[self.offset] & 0xFF
  62. def __str__(self):
  63. return "profibus.%s" % self.halName
  64. class SigU16(object):
  65. def __init__(self, hal, halName, offset):
  66. self.hal = hal
  67. self.halName = halName
  68. self.offset = offset
  69. def fromHal(self, destBuf):
  70. word = self.hal[self.halName] & 0xFFFF
  71. destBuf[self.offset] = (word >> 8) & 0xFF
  72. destBuf[self.offset + 1] = word & 0xFF
  73. def toHal(self, srcBuf):
  74. word = (srcBuf[self.offset] << 8) |\
  75. srcBuf[self.offset + 1]
  76. self.hal[self.halName] = word & 0xFFFF
  77. def __str__(self):
  78. return "profibus.%s" % self.halName
  79. class SigS16(object):
  80. def __init__(self, hal, halName, offset):
  81. self.hal = hal
  82. self.halName = halName
  83. self.offset = offset
  84. def fromHal(self, destBuf):
  85. word = self.hal[self.halName] & 0xFFFF
  86. destBuf[self.offset] = (word >> 8) & 0xFF
  87. destBuf[self.offset + 1] = word & 0xFF
  88. def toHal(self, srcBuf):
  89. word = (srcBuf[self.offset] << 8) |\
  90. srcBuf[self.offset + 1]
  91. if word & 0x8000:
  92. self.hal[self.halName] = -((~word + 1) & 0xFFFF)
  93. else:
  94. self.hal[self.halName] = word & 0xFFFF
  95. def __str__(self):
  96. return "profibus.%s" % self.halName
  97. class SigU31(object):
  98. def __init__(self, hal, halName, offset):
  99. self.hal = hal
  100. self.halName = halName
  101. self.offset = offset
  102. def fromHal(self, destBuf):
  103. dword = self.hal[self.halName] & 0x7FFFFFFF
  104. destBuf[self.offset] = (dword >> 24) & 0xFF
  105. destBuf[self.offset + 1] = (dword >> 16) & 0xFF
  106. destBuf[self.offset + 2] = (dword >> 8) & 0xFF
  107. destBuf[self.offset + 3] = dword & 0xFF
  108. def toHal(self, srcBuf):
  109. dword = (srcBuf[self.offset] << 24) |\
  110. (srcBuf[self.offset + 1] << 16) |\
  111. (srcBuf[self.offset + 2] << 8) |\
  112. srcBuf[self.offset + 3]
  113. self.hal[self.halName] = dword & 0x7FFFFFFF
  114. def __str__(self):
  115. return "profibus.%s" % self.halName
  116. class SigS32(object):
  117. def __init__(self, hal, halName, offset):
  118. self.hal = hal
  119. self.halName = halName
  120. self.offset = offset
  121. def fromHal(self, destBuf):
  122. dword = self.hal[self.halName] & 0xFFFFFFFF
  123. destBuf[self.offset] = (dword >> 24) & 0xFF
  124. destBuf[self.offset + 1] = (dword >> 16) & 0xFF
  125. destBuf[self.offset + 2] = (dword >> 8) & 0xFF
  126. destBuf[self.offset + 3] = dword & 0xFF
  127. def toHal(self, srcBuf):
  128. dword = (srcBuf[self.offset] << 24) |\
  129. (srcBuf[self.offset + 1] << 16) |\
  130. (srcBuf[self.offset + 2] << 8) |\
  131. srcBuf[self.offset + 3]
  132. if dword & 0x80000000:
  133. self.hal[self.halName] = -((~dword + 1) & 0xFFFFFFFF)
  134. else:
  135. self.hal[self.halName] = dword & 0xFFFFFFFF
  136. def __str__(self):
  137. return "profibus.%s" % self.halName
  138. class SigFloat(object):
  139. floatStruct = struct.Struct(str('>f'))
  140. def __init__(self, hal, halName, offset):
  141. self.hal = hal
  142. self.halName = halName
  143. self.offset = offset
  144. def fromHal(self, destBuf):
  145. buf = self.floatStruct.pack(self.hal[self.halName])
  146. if isPy2Compat:
  147. buf = [ ord(b) for b in buf ]
  148. destBuf[self.offset : self.offset + 4] = buf[0 : 4]
  149. def toHal(self, srcBuf):
  150. dword = (srcBuf[self.offset] << 24) |\
  151. (srcBuf[self.offset + 1] << 16) |\
  152. (srcBuf[self.offset + 2] << 8) |\
  153. srcBuf[self.offset + 3]
  154. if isPy2Compat:
  155. value = self.floatStruct.unpack(
  156. chr((dword >> 24) & 0xFF) +\
  157. chr((dword >> 16) & 0xFF) +\
  158. chr((dword >> 8) & 0xFF) +\
  159. chr(dword & 0xFF)
  160. )[0]
  161. else:
  162. value = self.floatStruct.unpack(
  163. bytes( ((dword >> 24) & 0xFF,
  164. (dword >> 16) & 0xFF,
  165. (dword >> 8) & 0xFF,
  166. dword & 0xFF)
  167. )
  168. )[0]
  169. self.hal[self.halName] = value
  170. def __str__(self):
  171. return "profibus.%s" % self.halName
  172. class Worker(object):
  173. def __init__(self, hal, master):
  174. self.__configDone = False
  175. self.hal = hal
  176. self.master = master
  177. self.slaves = master.getSlaveList()
  178. def __buildTable(self, slaveAddr, direction, size):
  179. tab = []
  180. for i in range(0, size):
  181. for bitNr in range(8):
  182. halName = "slave.%d.%s.bit.%d.%d" % (
  183. slaveAddr, direction, i, bitNr)
  184. if self.hal[halName + ".active"]:
  185. tab.append(SigBit(self.hal, halName,
  186. i, bitNr))
  187. halName = "slave.%d.%s.u8.%d" % (
  188. slaveAddr, direction, i)
  189. if self.hal[halName + ".active"]:
  190. tab.append(SigU8(self.hal, halName, i))
  191. if i % 2:
  192. continue
  193. if size - i < 2:
  194. continue
  195. halName = "slave.%d.%s.u16.%d" % (
  196. slaveAddr, direction, i)
  197. if self.hal[halName + ".active"]:
  198. tab.append(SigU16(self.hal, halName, i))
  199. halName = "slave.%d.%s.s16.%d" % (
  200. slaveAddr, direction, i)
  201. if self.hal[halName + ".active"]:
  202. tab.append(SigS16(self.hal, halName, i))
  203. if size - i < 4:
  204. continue
  205. halName = "slave.%d.%s.u31.%d" % (
  206. slaveAddr, direction, i)
  207. if self.hal[halName + ".active"]:
  208. tab.append(SigU31(self.hal, halName, i))
  209. halName = "slave.%d.%s.s32.%d" % (
  210. slaveAddr, direction, i)
  211. if self.hal[halName + ".active"]:
  212. tab.append(SigS32(self.hal, halName, i))
  213. halName = "slave.%d.%s.float.%d" % (
  214. slaveAddr, direction, i)
  215. if self.hal[halName + ".active"]:
  216. tab.append(SigFloat(self.hal, halName, i))
  217. return tab
  218. def __tryBuildConfig(self):
  219. if not self.hal["config.ready"]:
  220. return
  221. for slave in self.slaves:
  222. slaveConf = slave._slaveConf
  223. activePbInputs = self.__buildTable(
  224. slave.slaveAddr, "input", slaveConf.inputSize)
  225. activePbOutputs = self.__buildTable(
  226. slave.slaveAddr, "output", slaveConf.outputSize)
  227. slave._activePbInputs = activePbInputs
  228. slave._activePbOutputs = activePbOutputs
  229. printInfo("Active DP slave (addr=%d) pins:" % slave.slaveAddr)
  230. for sig in activePbInputs:
  231. printInfo("DP input: " + str(sig))
  232. for sig in activePbOutputs:
  233. printInfo("DP output: " + str(sig))
  234. self.__configDone = True
  235. printInfo("HAL configuration done")
  236. def mainLoop(self):
  237. master = self.master
  238. while watchdog() and not self.__configDone:
  239. self.__tryBuildConfig()
  240. time.sleep(0.1)
  241. while watchdog():
  242. for slave in self.slaves:
  243. slaveConf = slave._slaveConf
  244. outputSize, inputSize = \
  245. slaveConf.outputSize, slaveConf.inputSize
  246. txData = bytearray((0,) * outputSize)
  247. for sig in slave._activePbOutputs:
  248. sig.fromHal(txData)
  249. rxData = master.runSlave(slave, txData)
  250. if rxData is not None:
  251. if len(rxData) > inputSize:
  252. rxData = rxData[0 : inputSize]
  253. if len(rxData) < inputSize:
  254. rxData += b'\0' * (inputSize - len(rxData))
  255. for sig in slave._activePbInputs:
  256. sig.toHal(rxData)
  257. class LinuxCNC_NotRunning(Exception):
  258. pass
  259. def printError(msg):
  260. sys.stderr.write("pyprofibus: " + msg + "\n")
  261. def printWarning(msg):
  262. sys.stderr.write("pyprofibus: " + msg + "\n")
  263. def printInfo(msg):
  264. sys.stdout.write("pyprofibus: " + msg + "\n")
  265. # Check presence of LinuxCNC.
  266. # Returns normally, if LinuxCNC is detected.
  267. # Raises LinuxCNC_NotRunning, if LinuxCNC is not detected.
  268. def watchdog():
  269. # Check whether LinuxCNC is running.
  270. for lockname in ("/tmp/linuxcnc.lock", "/tmp/emc.lock"):
  271. if fileExists(lockname):
  272. return True
  273. if not opt_watchdog:
  274. # The check is disabled. Return success.
  275. return True
  276. printError("LinuxCNC doesn't seem to be running. "\
  277. "(Use '--watchdog off' to disable this check.)")
  278. raise LinuxCNC_NotRunning()
  279. # Create the LinuxCNC HAL pins
  280. def createHalPins(hal, slaveAddr, outputSize, inputSize):
  281. HAL_BIT, HAL_U32, HAL_S32, HAL_FLOAT = \
  282. LinuxCNC_HAL.HAL_BIT, LinuxCNC_HAL.HAL_U32, \
  283. LinuxCNC_HAL.HAL_S32, LinuxCNC_HAL.HAL_FLOAT
  284. HAL_IN, HAL_OUT, HAL_RO, HAL_RW = \
  285. LinuxCNC_HAL.HAL_IN, LinuxCNC_HAL.HAL_OUT, \
  286. LinuxCNC_HAL.HAL_RO, LinuxCNC_HAL.HAL_RW
  287. addr = slaveAddr
  288. printInfo("DP slave %d output: %d bytes" % (addr, outputSize))
  289. printInfo("DP slave %d input: %d bytes" % (addr, inputSize))
  290. # Create the input pins
  291. for i in range(0, inputSize):
  292. for bit in range(8):
  293. hal.newpin("slave.%d.input.bit.%d.%d" % (addr, i, bit),
  294. HAL_BIT, HAL_OUT)
  295. hal.newparam("slave.%d.input.bit.%d.%d.active" % (addr, i, bit),
  296. HAL_BIT, HAL_RW)
  297. hal.newpin("slave.%d.input.u8.%d" % (addr, i),
  298. HAL_U32, HAL_OUT)
  299. hal.newparam("slave.%d.input.u8.%d.active" % (addr, i),
  300. HAL_BIT, HAL_RW)
  301. if i % 2:
  302. continue
  303. if inputSize - i < 2:
  304. continue
  305. hal.newpin("slave.%d.input.u16.%d" % (addr, i),
  306. HAL_U32, HAL_OUT)
  307. hal.newparam("slave.%d.input.u16.%d.active" % (addr, i),
  308. HAL_BIT, HAL_RW)
  309. hal.newpin("slave.%d.input.s16.%d" % (addr, i),
  310. HAL_S32, HAL_OUT)
  311. hal.newparam("slave.%d.input.s16.%d.active" % (addr, i),
  312. HAL_BIT, HAL_RW)
  313. if inputSize - i < 4:
  314. continue
  315. hal.newpin("slave.%d.input.u31.%d" % (addr, i),
  316. HAL_U32, HAL_OUT)
  317. hal.newparam("slave.%d.input.u31.%d.active" % (addr, i),
  318. HAL_BIT, HAL_RW)
  319. hal.newpin("slave.%d.input.s32.%d" % (addr, i),
  320. HAL_S32, HAL_OUT)
  321. hal.newparam("slave.%d.input.s32.%d.active" % (addr, i),
  322. HAL_BIT, HAL_RW)
  323. hal.newpin("slave.%d.input.float.%d" % (addr, i),
  324. HAL_FLOAT, HAL_OUT)
  325. hal.newparam("slave.%d.input.float.%d.active" % (addr, i),
  326. HAL_BIT, HAL_RW)
  327. # Create the output pins
  328. for i in range(0, outputSize):
  329. for bit in range(8):
  330. hal.newpin("slave.%d.output.bit.%d.%d" % (addr, i, bit),
  331. HAL_BIT, HAL_IN)
  332. hal.newparam("slave.%d.output.bit.%d.%d.active" % (addr, i, bit),
  333. HAL_BIT, HAL_RW)
  334. hal.newpin("slave.%d.output.u8.%d" % (addr, i),
  335. HAL_U32, HAL_IN)
  336. hal.newparam("slave.%d.output.u8.%d.active" % (addr, i),
  337. HAL_BIT, HAL_RW)
  338. if i % 2:
  339. continue
  340. if outputSize < 2:
  341. continue
  342. hal.newpin("slave.%d.output.u16.%d" % (addr, i),
  343. HAL_U32, HAL_IN)
  344. hal.newparam("slave.%d.output.u16.%d.active" % (addr, i),
  345. HAL_BIT, HAL_RW)
  346. hal.newpin("slave.%d.output.s16.%d" % (addr, i),
  347. HAL_S32, HAL_IN)
  348. hal.newparam("slave.%d.output.s16.%d.active" % (addr, i),
  349. HAL_BIT, HAL_RW)
  350. if outputSize < 4:
  351. continue
  352. hal.newpin("slave.%d.output.u31.%d" % (addr, i),
  353. HAL_U32, HAL_IN)
  354. hal.newparam("slave.%d.output.u31.%d.active" % (addr, i),
  355. HAL_BIT, HAL_RW)
  356. hal.newpin("slave.%d.output.s32.%d" % (addr, i),
  357. HAL_S32, HAL_IN)
  358. hal.newparam("slave.%d.output.s32.%d.active" % (addr, i),
  359. HAL_BIT, HAL_RW)
  360. hal.newpin("slave.%d.output.float.%d" % (addr, i),
  361. HAL_FLOAT, HAL_IN)
  362. hal.newparam("slave.%d.output.float.%d.active" % (addr, i),
  363. HAL_BIT, HAL_RW)
  364. hal.newparam("config.ready", HAL_BIT, HAL_RW)
  365. def usage():
  366. print("pyprofibus-linuxcnc-hal version %s" % VERSION_STRING)
  367. print("")
  368. print("Usage: pyprofibus-linuxcnc-hal [OPTIONS] pyprofibus.conf")
  369. print("")
  370. print("Options:")
  371. print("")
  372. print(" -L|--loglevel LVL Set the log level:")
  373. print(" 0: Log nothing")
  374. print(" 1: Log errors")
  375. print(" 2: Log errors and warnings")
  376. print(" 3: Log errors, warnings and info messages (default)")
  377. print(" 4: Verbose logging")
  378. print(" 5: Extremely verbose logging")
  379. print(" -N|--nice NICE Renice the process. -20 <= NICE <= 19.")
  380. print(" Default: Do not renice")
  381. print("")
  382. print("Debugging options:")
  383. print(" -W|--watchdog 1/0 Enable/disable LinuxCNC runtime watchdog.")
  384. print(" Default: on")
  385. print("")
  386. print("For an example LinuxCNC HAL configuration see:")
  387. print(" linuxcnc-demo.hal")
  388. def main():
  389. global LinuxCNC_HAL
  390. global opt_loglevel
  391. global opt_nice
  392. global opt_watchdog
  393. opt_loglevel = 3
  394. opt_nice = None
  395. opt_watchdog = True
  396. try:
  397. (opts, args) = getopt.getopt(sys.argv[1:],
  398. "hL:N:W:",
  399. [ "help",
  400. "loglevel=",
  401. "nice=",
  402. "watchdog=", ])
  403. except getopt.GetoptError as e:
  404. printError(str(e))
  405. usage()
  406. return 1
  407. for (o, v) in opts:
  408. if o in ("-h", "--help"):
  409. usage()
  410. return 0
  411. if o in ("-L", "--loglevel"):
  412. try:
  413. opt_loglevel = int(v)
  414. except ValueError:
  415. printError("-L|--loglevel: Invalid log level")
  416. return 1
  417. if o in ("-N", "--nice"):
  418. try:
  419. opt_nice = int(v)
  420. if opt_nice < -20 or opt_nice > 19:
  421. raise ValueError
  422. except ValueError:
  423. printError("-N|--nice: Invalid niceness level")
  424. return 1
  425. if o in ("-W", "--watchdog"):
  426. opt_watchdog = str2bool(v)
  427. if len(args) != 1:
  428. usage()
  429. return 1
  430. configFile = args[0]
  431. result = 0
  432. try:
  433. # Parse the Profibus config file
  434. config = PbConf.fromFile(configFile)
  435. # Adjust process priority
  436. if opt_nice is not None:
  437. try:
  438. os.nice(opt_nice)
  439. except OSError as e:
  440. printError("Failed to renice process to "
  441. "%d: %s" % (opt_nice, str(e)))
  442. return 1
  443. # Try to import the LinuxCNC HAL module
  444. try:
  445. import hal as LinuxCNC_HAL
  446. except ImportError as e:
  447. printError("Failed to import LinuxCNC HAL "
  448. "module: %s" % str(e))
  449. return 1
  450. # Create the LinuxCNC HAL component.
  451. hal = LinuxCNC_HAL.component("profibus")
  452. # Create the HAL pins.
  453. for slaveConf in config.slaveConfs:
  454. createHalPins(hal = hal,
  455. slaveAddr = slaveConf.addr,
  456. inputSize = slaveConf.inputSize,
  457. outputSize = slaveConf.outputSize)
  458. # Setup the PROFIBUS stack.
  459. phy = config.makePhy()
  460. if config.dpMasterClass == 1:
  461. DPM = DPM1
  462. else:
  463. DPM = DPM2
  464. master = DPM(phy = phy,
  465. masterAddr = config.dpMasterAddr,
  466. debug = (opt_loglevel >= 4))
  467. for slaveConf in config.slaveConfs:
  468. gsd = slaveConf.gsd
  469. slave = DpSlaveDesc(
  470. identNumber = gsd.getIdentNumber(),
  471. slaveAddr = slaveConf.addr)
  472. slave.setCfgDataElements(
  473. gsd.getCfgDataElements())
  474. dp1PrmMask = bytearray((
  475. DpTelegram_SetPrm_Req.DPV1PRM0_FAILSAFE,
  476. DpTelegram_SetPrm_Req.DPV1PRM1_REDCFG,
  477. 0x00))
  478. dp1PrmSet = bytearray((
  479. DpTelegram_SetPrm_Req.DPV1PRM0_FAILSAFE,
  480. DpTelegram_SetPrm_Req.DPV1PRM1_REDCFG,
  481. 0x00))
  482. slave.setUserPrmData(
  483. gsd.getUserPrmData(dp1PrmMask = dp1PrmMask,
  484. dp1PrmSet = dp1PrmSet))
  485. slave.setSyncMode(slaveConf.syncMode)
  486. slave.setFreezeMode(slaveConf.freezeMode)
  487. slave.setGroupMask(slaveConf.groupMask)
  488. slave.setWatchdog(slaveConf.watchdogMs)
  489. slave._slaveConf = slaveConf
  490. master.addSlave(slave)
  491. printInfo("Running PROFIBUS-DP master...")
  492. master.initialize()
  493. worker = Worker(hal, master)
  494. hal.ready()
  495. printInfo("ready.")
  496. lastExceptionTime = None
  497. while True:
  498. try:
  499. worker.mainLoop()
  500. except ProfibusError as e:
  501. now = monotonic_time()
  502. if lastExceptionTime is not None and\
  503. now - lastExceptionTime < 1.0:
  504. # The last fault is less than one second
  505. # in the past. Raise a fatal exception.
  506. printError("Fatal fault detected")
  507. raise e
  508. else:
  509. lastExceptionTime = now
  510. # Non-fatal fault.
  511. printError("PROFIBUS fault:\n%s" % str(e))
  512. except LinuxCNC_NotRunning as e:
  513. result = 1
  514. except KeyboardInterrupt as e:
  515. result = 1
  516. except PbConfError as e:
  517. printError("Profibus configuration error:\n%s" % str(e))
  518. result = 1
  519. except ProfibusError as e:
  520. printError("Fatal PROFIBUS fault:\n%s" % str(e))
  521. result = 1
  522. printInfo("LinuxCNC HAL module shutdown.")
  523. return result
  524. if __name__ == "__main__":
  525. sys.exit(main())