--- a/plugger.py Thu Sep 04 16:07:14 2008 +0200
+++ b/plugger.py Fri Sep 05 16:25:57 2008 +0200
@@ -600,7 +600,7 @@
ieclib_path = os.path.join(base_folder, "matiec", "lib")
# import for project creation timestamping
-from threading import Timer, Lock, Thread
+from threading import Timer, Lock, Thread, Semaphore
from time import localtime
from datetime import datetime
# import necessary stuff from PLCOpenEditor
@@ -1054,62 +1054,6 @@
for v in self._VariablesList if v["type"] in DebugTypes ])}
return debug_code
-
- def RegisterDebugVarToConnector(self):
- self.DebugTimer=None
- Idxs = []
- self.TracedIECPath = []
- if self._connector is not None:
- self.IECdebug_lock.acquire()
- for IECPath,data_tuple in self.IECdebug_datas.iteritems():
- WeakCallableDict, data_log, status = data_tuple
- if len(WeakCallableDict) == 0:
- # Callable Dict is empty.
- # This variable is not needed anymore!
- # self.IECdebug_callables.pop(IECPath)
- # TODO
- print "Unused : " + IECPath
- else:
- # Convert
- Idx = self._IECPathToIdx.get(IECPath,None)
- if Idx is not None:
- Idxs.append(Idx)
- self.TracedIECPath.append(IECPath)
- else:
- self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath)
- self._connector.SetTraceVariablesList(Idxs)
- self.IECdebug_lock.release()
-
- def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs):
- """
- Dispatching use a dictionnary linking IEC variable paths
- to a WeakKeyDictionary linking
- weakly referenced callables to optionnal args
- """
- self.IECdebug_lock.acquire()
- # If no entry exist, create a new one with a fresh WeakKeyDictionary
- IECdebug_data = self.IECdebug_datas.get(IECPath, None)
- if IECdebug_data is None:
- IECdebug_data = [
- WeakKeyDictionary(), # Callables
- [], # Data storage [(tick, data),...]
- "Registered"] # Variable status
- self.IECdebug_datas[IECPath] = IECdebug_data
-
- IECdebug_data[0][callableobj]=(args, kwargs)
-
- self.IECdebug_lock.release()
-
- if self.DebugTimer is not None:
- self.DebugTimer.cancel()
-
- # Timer to prevent rapid-fire when registering many variables
- # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
- self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector])
- # Rearm anti-rapid-fire timer
- self.DebugTimer.start()
-
- return IECdebug_data[1]
def Generate_plc_common_main(self):
"""
@@ -1337,10 +1281,85 @@
self.logger.write_error("Couldn't start PLC !\n")
self.UpdateMethodsFromPLCStatus()
+ def RegisterDebugVarToConnector(self):
+ self.DebugTimer=None
+ Idxs = []
+ self.TracedIECPath = []
+ if self._connector is not None:
+ self.IECdebug_lock.acquire()
+ IECPathsToPop = []
+ for IECPath,data_tuple in self.IECdebug_datas.iteritems():
+ WeakCallableDict, data_log, status = data_tuple
+ if len(WeakCallableDict) == 0:
+ # Callable Dict is empty.
+ # This variable is not needed anymore!
+ #print "Unused : " + IECPath
+ IECPathsToPop.append(IECPath)
+ else:
+ # Convert
+ Idx = self._IECPathToIdx.get(IECPath,None)
+ if Idx is not None:
+ Idxs.append(Idx)
+ self.TracedIECPath.append(IECPath)
+ else:
+ self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath)
+ for IECPathToPop in IECPathsToPop:
+ self.IECdebug_datas.pop(IECPathToPop)
+
+ self._connector.SetTraceVariablesList(Idxs)
+ self.IECdebug_lock.release()
+
+ def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs):
+ """
+ Dispatching use a dictionnary linking IEC variable paths
+ to a WeakKeyDictionary linking
+ weakly referenced callables to optionnal args
+ """
+ self.IECdebug_lock.acquire()
+ # If no entry exist, create a new one with a fresh WeakKeyDictionary
+ IECdebug_data = self.IECdebug_datas.get(IECPath, None)
+ if IECdebug_data is None:
+ IECdebug_data = [
+ WeakKeyDictionary(), # Callables
+ [], # Data storage [(tick, data),...]
+ "Registered"] # Variable status
+ self.IECdebug_datas[IECPath] = IECdebug_data
+
+ IECdebug_data[0][callableobj]=(args, kwargs)
+
+ self.IECdebug_lock.release()
+
+ if self.DebugTimer is not None:
+ self.DebugTimer.cancel()
+
+ # Timer to prevent rapid-fire when registering many variables
+ # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
+ self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector])
+ # Rearm anti-rapid-fire timer
+ self.DebugTimer.start()
+
+ return IECdebug_data[1]
+
+ def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
+ IECdebug_data = self.IECdebug_datas.get(IECPath, None)
+ if IECdebug_data is None:
+ IECdebug_data[0].pop(callableobj,None)
+
+ def DebugCallerFunc(self, weakcallable, value, *args, **kwargs):
+ # do the call
+ weakcallable.SetValue(value, *args, **kwargs)
+ # will unlock debug thread
+ self.DebugThreadSlowDownLock.release()
+
def DebugThreadProc(self):
+ """
+ This thread waid PLC debug data, and dispatch them to subscribers
+ """
+ # This lock is used to avoid flooding wx event stack calling callafter
+ self.DebugThreadSlowDownLock = Semaphore(0)
while self._connector is not None:
debug_tick, debug_vars = self._connector.GetTraceVariables()
- print debug_tick, debug_vars
+ #print debug_tick, debug_vars
if debug_vars is not None and \
len(debug_vars) == len(self.TracedIECPath):
for IECPath,value in zip(self.TracedIECPath, debug_vars):
@@ -1349,13 +1368,16 @@
WeakCallableDict, data_log, status = data_tuple
data_log.append((debug_tick, value))
for weakcallable,(args,kwargs) in WeakCallableDict.iteritems():
- wx.CallAfter(weakcallable.SetValue, value, *args, **kwargs)
+ # delegate call to wx event loop
+ wx.CallAfter(self.DebugCallerFunc, weakcallable, value, *args, **kwargs)
+ # This will block thread if more than one call is waiting
+ self.DebugThreadSlowDownLock.acquire()
elif debug_vars is not None:
wx.CallAfter(self.logger.write_warning,
"debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath)))
- elif debug_tick == -1:
+ #elif debug_tick == -1:
#wx.CallAfter(self.logger.write, "Debugger unavailable\n")
- pass
+ # pass
else:
wx.CallAfter(self.logger.write, "Debugger disabled\n")
break
@@ -1367,6 +1389,7 @@
if self.GetIECProgramsAndVariables() and \
self._connector.StartPLC(debug=True):
self.logger.write("Starting PLC (debug mode)\n")
+ self.TracedIECPath = []
# TODO : laucnch PLCOpenEditor in Debug Mode
self.DebugThread = Thread(target=self.DebugThreadProc)
self.DebugThread.start()
@@ -1379,12 +1402,13 @@
# self.temporary_non_weak_callable_refs = []
# for IEC_Path, idx in self._IECPathToIdx.iteritems():
# class tmpcls:
-# def __init__(self):
+# def __init__(_self):
# self.buf = None
-# def setbuf(self,buf):
+# def setbuf(_self,buf):
# self.buf = buf
-# def SetValue(self, value, idx, name):
-# print "debug call:", value, idx, name, self.buf
+# def SetValue(_self, value, idx, name):
+# self.logger.write("debug call: %s %d %s\n"%(repr(value), idx, name))
+# #self.logger.write("debug call: %s %d %s %s\n"%(repr(value), idx, name, repr(self.buf)))
# a = tmpcls()
# res = self.SubscribeDebugIECVariable(IEC_Path, a, idx, IEC_Path)
# a.setbuf(res)
--- a/runtime/PLCObject.py Thu Sep 04 16:07:14 2008 +0200
+++ b/runtime/PLCObject.py Fri Sep 05 16:25:57 2008 +0200
@@ -302,9 +302,9 @@
for given_idx in self._Idxs:
buffer=self._IterDebugData(ctypes.byref(idx), ctypes.byref(typename))
- c_type,unpack_func = self.TypeTranslator.get(typename.value, None)
+ c_type,unpack_func = self.TypeTranslator.get(typename.value, (None,None))
if c_type is not None and given_idx == idx.value:
- res.append(unpack_func(ctypes.cast(buffer,
+ res.append(unpack_func(ctypes.cast(buffer,
ctypes.POINTER(c_type)).contents))
else:
print "Debug error idx : %d, expected_idx %d, type : %s"%(idx.value, given_idx,typename.value)
--- a/targets/Linux/plc_Linux_main.c Thu Sep 04 16:07:14 2008 +0200
+++ b/targets/Linux/plc_Linux_main.c Fri Sep 05 16:25:57 2008 +0200
@@ -61,6 +61,12 @@
exit(0);
}
+
+static int __debug_tick;
+
+static pthread_mutex_t wait_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
+
int startPLC(int argc,char **argv)
{
struct sigevent sigev;
@@ -73,6 +79,8 @@
sigev.sigev_notify_attributes = NULL;
sigev.sigev_notify_function = PLC_timer_notify;
+ pthread_mutex_lock(&wait_mutex);
+
timer_create (CLOCK_REALTIME, &sigev, &PLC_timer);
if( __init(argc,argv) == 0 ){
PLC_SetTimer(Ttick,Ttick);
@@ -86,18 +94,14 @@
return 0;
}
-static int __debug_tick;
+int TryEnterDebugSection(void)
+{
+ return pthread_mutex_trylock(&debug_mutex) == 0;
+}
-static pthread_mutex_t wait_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t wait_cond = PTHREAD_COND_INITIALIZER;
-
-void AbortDebug()
+void LeaveDebugSection(void)
{
- /* Eventually unlock debugger thread*/
- __debug_tick = -1;
- //pthread_mutex_lock(&wait_mutex);
- pthread_cond_broadcast(&wait_cond);
- //pthread_mutex_unlock(&wait_mutex);
+ pthread_mutex_unlock(&debug_mutex);
}
int stopPLC()
@@ -106,7 +110,8 @@
PLC_SetTimer(0,0);
timer_delete (PLC_timer);
__cleanup();
- AbortDebug();
+ __debug_tick = -1;
+ pthread_mutex_unlock(&wait_mutex);
}
extern int __tick;
@@ -115,8 +120,6 @@
{
/* Wait signal from PLC thread */
pthread_mutex_lock(&wait_mutex);
- pthread_cond_wait(&wait_cond, &wait_mutex);
- pthread_mutex_unlock(&wait_mutex);
return __debug_tick;
}
@@ -124,9 +127,23 @@
* This is supposed to unlock debugger thread in WaitDebugData*/
void InitiateDebugTransfer()
{
- /* signal debugger thread to continue*/
+ /* Leave debugger section */
+ pthread_mutex_unlock(&debug_mutex);
+ /* remember tick */
__debug_tick = __tick;
- //pthread_mutex_lock(&wait_mutex);
- pthread_cond_broadcast(&wait_cond);
- //pthread_mutex_unlock(&wait_mutex);
+ /* signal debugger thread it can read data */
+ pthread_mutex_unlock(&wait_mutex);
}
+
+void suspendDebug()
+{
+ /* Prevent PLC to enter debug code */
+ pthread_mutex_lock(&debug_mutex);
+}
+
+void resumeDebug()
+{
+ /* Let PLC enter debug code */
+ pthread_mutex_unlock(&debug_mutex);
+}
+
--- a/targets/plc_common_main.c Thu Sep 04 16:07:14 2008 +0200
+++ b/targets/plc_common_main.c Fri Sep 05 16:25:57 2008 +0200
@@ -54,13 +54,11 @@
%(retrieve_calls)s
- if(Debugging) __retrieve_debug();
+ /*__retrieve_debug();*/
config_run__(__tick);
- if(Debugging) __publish_debug();
- else if(WasDebugging) AbortDebug();
- WasDebugging = Debugging;
+ __publish_debug();
%(publish_calls)s
@@ -173,18 +171,3 @@
}
}
}
-
-extern int WaitDebugData();
-void suspendDebug()
-{
- /* Prevent PLC to enter debug code */
- Debugging = 0;
- /* wait next tick end to be sure*/
- WaitDebugData();
-}
-
-void resumeDebug()
-{
- /* Let PLC enter debug code */
- Debugging = 1;
-}
--- a/targets/plc_debug.c Thu Sep 04 16:07:14 2008 +0200
+++ b/targets/plc_debug.c Fri Sep 05 16:25:57 2008 +0200
@@ -63,52 +63,62 @@
{
}
+extern int TryEnterDebugSection(void);
+extern void LeaveDebugSection(void);
+
+extern int __tick;
void __publish_debug()
{
- /* Lock buffer */
- long latest_state = AtomicCompareExchange(
- &buffer_state,
- BUFFER_FREE,
- BUFFER_BUSY);
-
- /* If buffer was free */
- if(latest_state == BUFFER_FREE)
- {
- int* subscription;
-
- /* Reset buffer cursor */
- buffer_cursor = debug_buffer;
-
- /* iterate over subscriptions */
- for(subscription=subscription_table;
- subscription < latest_subscription;
- subscription++)
+ /* Check there is no running debugger re-configuration */
+ if(TryEnterDebugSection()){
+ /* Lock buffer */
+ long latest_state = AtomicCompareExchange(
+ &buffer_state,
+ BUFFER_FREE,
+ BUFFER_BUSY);
+
+ /* If buffer was free */
+ if(latest_state == BUFFER_FREE)
{
- /* get variable descriptor */
- struct_plcvar* my_var = &variable_table[*subscription];
- char* next_cursor;
- /* get variable size*/
- USINT size = __get_type_enum_size(my_var->type);
- /* compute next cursor positon*/
- next_cursor = buffer_cursor + size;
- /* if buffer not full */
- if(next_cursor < debug_buffer + BUFFER_SIZE)
+ int* subscription;
+
+ /* Reset buffer cursor */
+ buffer_cursor = debug_buffer;
+
+ /* iterate over subscriptions */
+ for(subscription=subscription_table;
+ subscription < latest_subscription;
+ subscription++)
{
- /* copy data to the buffer */
- memcpy(buffer_cursor, my_var->ptrvalue, size);
- /* increment cursor according size*/
- buffer_cursor = next_cursor;
- }else{
- /*TODO : signal overflow*/
+ /* get variable descriptor */
+ struct_plcvar* my_var = &variable_table[*subscription];
+ char* next_cursor;
+ /* get variable size*/
+ USINT size = __get_type_enum_size(my_var->type);
+ /* compute next cursor positon*/
+ next_cursor = buffer_cursor + size;
+ /* if buffer not full */
+ if(next_cursor < debug_buffer + BUFFER_SIZE)
+ {
+ /* copy data to the buffer */
+ memcpy(buffer_cursor, my_var->ptrvalue, size);
+ /* increment cursor according size*/
+ buffer_cursor = next_cursor;
+ }else{
+ /*TODO : signal overflow*/
+ }
}
+
+ /* Reset buffer cursor again (for IterDebugData)*/
+ buffer_cursor = debug_buffer;
+ subscription_cursor = subscription_table;
+
+ /* Leave debug section,
+ * Trigger asynchronous transmission
+ * (returns immediately) */
+ InitiateDebugTransfer(); /* size */
}
-
- /* Reset buffer cursor again (for IterDebugData)*/
- buffer_cursor = debug_buffer;
- subscription_cursor = subscription_table;
-
- /* Trigger asynchronous transmission (returns immediately) */
- InitiateDebugTransfer(); /* size */
+ LeaveDebugSection();
}
}