Skip to content

Commit

Permalink
WIP adding addLog/Lambda technique
Browse files Browse the repository at this point in the history
  • Loading branch information
gerth2 committed Oct 19, 2024
1 parent 080c533 commit 9d0edf7
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 43 deletions.
21 changes: 9 additions & 12 deletions robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from humanInterface.ledControl import LEDControl
from navigation.forceGenerators import PointObstacle
from utils.segmentTimeTracker import SegmentTimeTracker
from utils.signalLogging import SignalWrangler, log
from utils.signalLogging import log, addLog, update
import utils.signalLogging
from utils.calibration import CalibrationWrangler
from utils.faults import FaultWrangler
Expand Down Expand Up @@ -66,12 +66,11 @@ def robotInit(self):

def _testLoggingInit(self):
for i in range(0,100):
log(f"testSig{i}", i)
addLog(f"testSig{i}", lambda: self.autoHasRun)

def _testLoggingMany(self):
for _ in range (0,100000):
for i in range(0,100):
log(f"testSig{i}", i)
for _ in range (0,1000):
update()


def robotPeriodic(self):
Expand Down Expand Up @@ -106,16 +105,18 @@ def autonomousInit(self):
self.autoHasRun = True

# Test only
stats = None

self._testLoggingInit()
profiler = LineProfiler()
profiler.add_module(utils.signalLogging)
start = wpilib.Timer.getFPGATimestamp()
profiler.runcall(self._testLoggingMany)
end = wpilib.Timer.getFPGATimestamp()
profiler.print_stats()
print(f"TotalTime: {end-start} sec")


def autonomousPeriodic(self):
SignalWrangler().markLoopStart()

self.autoSequencer.update()

Expand All @@ -141,8 +142,6 @@ def teleopInit(self):

def teleopPeriodic(self):

SignalWrangler().markLoopStart()

self.driveTrain.setManualCmd(self.dInt.getCmd())

if self.dInt.getGyroResetCmd():
Expand Down Expand Up @@ -172,7 +171,6 @@ def teleopPeriodic(self):
#########################################################
## Disabled-Specific init and update
def disabledPeriodic(self):
SignalWrangler().markLoopStart()
self.autoSequencer.updateMode()
Trajectory().trajCtrl.updateCals()

Expand All @@ -185,8 +183,7 @@ def testInit(self):
wpilib.LiveWindow.setEnabled(False)

def testPeriodic(self):
SignalWrangler().markLoopStart()
# Nothing else to do, main update does all the heavy lifting
pass

#########################################################
## Cleanup
Expand Down
72 changes: 41 additions & 31 deletions utils/signalLogging.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from dataclasses import dataclass
from typing import Callable
import wpilib
import ntcore as nt
import wpiutil.log as wpilog # pylint: disable=import-error,no-name-in-module
Expand All @@ -8,6 +10,11 @@

BASE_TABLE = "SmartDashboard"

@dataclass
class _LoggedVal():
valGetter:Callable[[], float]
ntPublisher:nt.DoublePublisher
filePublisher:wpilog.DoubleLogEntry|None

# Wrangler for coordinating the set of all signals
class SignalWrangler(metaclass=Singleton):
Expand All @@ -16,21 +23,27 @@ class SignalWrangler(metaclass=Singleton):
def __init__(self):
# Default to publishing things under Shuffleboard, which makes things more available
self.table = nt.NetworkTableInstance.getDefault().getTable(BASE_TABLE)
self.publishedSigDict = defaultdict()
self.loggedValList:list[_LoggedVal] = []
self.time = int(0)
self.log = None

#if ExtDriveManager().isConnected():
# wpilib.DataLogManager.start(dir=ExtDriveManager().getLogStoragePath())
# wpilib.DataLogManager.logNetworkTables(
# False
# ) # We have a lot of things in NT that don't need to be logged
# self.log = wpilib.DataLogManager.getLog()
if ExtDriveManager().isConnected():
wpilib.DataLogManager.start(dir=ExtDriveManager().getLogStoragePath())
wpilib.DataLogManager.logNetworkTables(
False
) # We have a lot of things in NT that don't need to be logged
self.log = wpilib.DataLogManager.getLog()

def markLoopStart(self):
self.time = nt._now() # pylint: disable=W0212
def update(self):
curTime = nt._now() # pylint: disable=W0212
for lv in self.loggedValList:
val = lv.valGetter()
lv.ntPublisher.set(val, curTime)
if(lv.filePublisher is not None):
lv.filePublisher.append(val, curTime)

def _newPublishedVal(self, name, units):

def newLogVal(self, name:str, valGetter:Callable[[],float], units:str|None):

# Set up NT publishing
sigTopic = self.table.getDoubleTopic(name)
Expand All @@ -50,26 +63,10 @@ def _newPublishedVal(self, name, units):
else:
sigLog = None

# Remember handles for both
sig = (sigPub, sigLog)
self.publishedSigDict[name] = sig
return sig

def publishValue(self, name, value, units):
#global sampIdx

try:
sig = self.publishedSigDict[name]
except KeyError:
sig = self._newPublishedVal(name, units)
self.loggedValList.append(
_LoggedVal(valGetter,sigPub, sigLog)
)

# Publish value to NT
ntsig = sig[0]
ntsig.set(value, self.time)
flsig = sig[1]
# Put value to log file
if flsig is not None:
flsig.append(value, self.time)



Expand All @@ -79,9 +76,22 @@ def publishValue(self, name, value, units):

_singletonInst = SignalWrangler() # cache a reference
# Log a new named value
def log(name, value, units=None):
_singletonInst.publishValue(name, value, units)
def update():
_singletonInst.update()

def addLog(alias: str, value_getter: Callable[[], float], units=None) -> None:
"""
Register some value to be loggd
Parameters:
- alias: The name used to identify the log.
- value_getter: A function that returns the current value of the log. Lambda is acceptable here.
"""
_singletonInst.newLogVal(alias, value_getter, units)


def log(_, __, ___=None):
pass # temp till we're done cleaning up

def sigNameToNT4TopicName(name):
return f"/{BASE_TABLE}/{name}"

0 comments on commit 9d0edf7

Please sign in to comment.