I just tried importing the Inventory Prediction Manager project. I've followed the steps from the README file, but I'm currently confused by the errors and how to configure everything to be able to use the tags I have. Can someone please help?https://inductiveautomation.com/exchange/2476/installation
We'll need more details to help you.
Show us :
- what the error is (put your mouse cursor over the red message on the bottom of the popup and wait for the tooltip to show),
- what that function called looks like (the
...createFullDataSet
one), - and what parameters you're passing to your binding.
Basically, I've just imported this "Resource Exchange" resource due to company needs. I've only configured the Gateway web page according to the installation steps provided by the creator.
You have a division by 0 on line 148.
Take a look at this:
def randomInventoryDecrementation(tagPath):#for simulation
#called from Simulator Trigger tag in UDT
value = system.tag.readBlocking([tagPath])[0].value
hour = system.date.getHour24(system.date.now())
if hour<12:#prevent "running out" every morning
hour = 24-hour
randomNum = 0.500/float(hour)
newValue = value-randomNum
if newValue<0:
newValue = 100.000
res = system.tag.writeBlocking([tagPath],[newValue])
return
def predictionDataset(tagPath = '[Inventory Management]Exchange/Inventory Management/Tank 1'):
fullDS = createFullDataset(tagPath)
#update tags
notificationTime = system.tag.readBlocking([tagPath+'/Order Lead Time'])[0].value
runoutDate = fullDS[-1]['time']
notificationDate = system.date.addDays(runoutDate, -notificationTime)
tagPaths = [
tagPath+'/Predicted Runout Date',
tagPath+'/Notification Date'
]
values = [
runoutDate,
notificationDate
]
#send notification
notificationSent = system.tag.readBlocking([tagPath+'/Notification Sent'])[0].value
if not notificationSent and system.date.now()>notificationTime:
sendNotification(tagPath)
tagPaths.append(tagPath+'/Notification Sent')
values.append(True)
writeQuality = system.tag.writeBlocking(tagPaths,values)
print writeQuality
return fullDS
def sendNotification(tagPath):
tagPaths = [
tagPath+'/Asset Name',
tagPath+'/Product Name',
tagPath+'/Predicted Runout Date',
tagPath+'/Order Lead Time',
tagPath+'/Notification Roster'
]
values = system.tag.readBlocking(tagPaths)
body = 'The predicted runout date for ' +str(values[0].value)+ '('+str(values[1].value)+') is '+ str(values[2].value)+'. With a lead time of '+str(values[3].value)+' days it is recommended that you re-order soon.'
recipients = values[4].value
smtp_server = "mySmtpServer"#configured in gateway
#uncomment to send notification
# system.net.sendEmail(smtpProfile=smtp_server, fromAddr="myemail@mycompany.com", subject="Inventory Alert", body=body, html=1, to=recipients)
return
def createFullDataset(tagPath = '[Inventory Management]Exchange/Inventory Management/Tank 1'):
#query tag history
typicalPeriod = system.tag.readBlocking([tagPath+'/Typical Period'])[0].value
tagPaths = [tagPath+'/Raw Value']
columnNames = ['value']
endDate = system.date.now()
startDate = system.date.addDays(endDate, int(-typicalPeriod))
aggregationMode = 'LastValue'
returnSize = -1
dataset = system.tag.queryTagHistory(tagPaths, startDate, endDate, columnNames = columnNames, aggregationMode = aggregationMode, returnSize = returnSize)
headers = system.dataset.getColumnHeaders(dataset)
pyds = system.dataset.toPyDataSet(dataset)
data = []
for row in pyds:
item = {
'time': row['t_stamp'],
'value': row['value']
}
data.append(item)
#find, if any, material refill points and remove previous data
threshold = system.tag.readBlocking([tagPath+'/Analysis Buffer Value'])[0].value
stopDict = data[0]
startIndex = 0
for startIndex in xrange(len(data)-1,0,-1):
if data[startIndex]['value']>threshold:
stopDict = data[startIndex-1]
break
# print 'stopDict: ',stopDict
# print
#initialize the return DS to the data before the analysis begins
fullDS = data[0:startIndex+1]
for row in fullDS:#prep full ds for calculated value and set to none
row['calculated'] = None
#get calculatedDS
#get the data that for the analysis to be performed on, data since last material fill
calculationDS = data[startIndex+1:len(data)]
calculationData = []
for item in calculationDS:
calculationData.append([item['time'],item['value']])
#get calculated dataset
datasetToCalculate = system.dataset.toDataSet(headers, calculationData)
calculatedDataset = calculateLinearRegressionOnHistoryDataset(datasetToCalculate)
calculatedpyds = system.dataset.toPyDataSet(calculatedDataset[0])
#append calcuated dataset to fullDS that was iniitialized above
for row in calculatedpyds:
fullDS.append({
'time': system.date.fromMillis(row['t_stamp']),
'value': row['value'],
'calculated': row['calculated']
})
#add y inercept data point for predition line
fullDS.append({
'time': system.date.fromMillis(int(calculatedDataset[2])),#y intercept
'value': None,
'calculated': 0
})
finalHeaders = ['t_stamp','value','calculated']
finalData = []
#view fully combined dataset
for row in fullDS:
print row
finalData.append([row['time'],row['value'],row['calculated']])
return fullDS
def linreg(X, Y):
if len(X) != len(Y): raise ValueError, 'unequal length'
N = len(X)
Sx = Sy = Sxx = Syy = Sxy = 0.0
for x, y in map(None, X, Y):
Sx = Sx + x
Sy = Sy + y
Sxx = Sxx + x*x
Syy = Syy + y*y
Sxy = Sxy + x*y
det = Sxx * N - Sx * Sx
a, b = (Sxy * N - Sy * Sx)/det, (Sxx * Sy - Sx * Sxy)/det
meanerror = residual = float(0.0)
for x, y in map(None, X, Y):
meanerror = meanerror + (y - Sy/N)**2
residual = residual + (y - a * x - b)**2
RR = 1 - residual/meanerror
ss = residual / (N-2)
Var_a, Var_b = ss * N / det, ss * Sxx / det
return a, b, RR #y intercept, slope, risk ratio
#dataset is expected to be a single tag historical query
#Use this function to perform linear regression on any historical dataset!
def calculateLinearRegressionOnHistoryDataset(dataset):
x = []
y=[]
for i in range(dataset.getRowCount()):
t_stamp = system.date.toMillis(dataset.getValueAt(i,0))
value = dataset.getValueAt(i,1)
# print t_stamp,value
y.append(t_stamp)#t_stamp, convert to millis
x.append(value)#tag history value
results = linreg(x,y)
slope = results[0]
yIntercept = results[1]
riskRatio = results[2]
print
# print results
print 'y intercept: ', yIntercept, system.date.fromMillis(int(yIntercept))
print 'slope: ',slope
print 'risk ratio: ',riskRatio
print
data = []
for i in range(dataset.getRowCount()):
t_stamp = system.date.toMillis(dataset.getValueAt(i,0))
value = dataset.getValueAt(i,1)
# calculated = slope*t_stamp+yIntercept
calculated = (t_stamp-yIntercept)/slope
# print t_stamp,value,calculated
data.append([t_stamp,value,calculated])
headers = system.dataset.getColumnHeaders(dataset)
headers.append('calculated')
ret = system.dataset.toDataSet(headers, data)
return ret,slope,yIntercept
are you succes with this module? i mean maybe will be some vid i can watch about this module
I don't use it.
I checked the code a little bit, and frankly I don't have time to debug it.
Here's a first step:
check the length of the data structures the linreg
function uses.
add some logging between lines 139 and 140 to log/print N
. This seems to me like the most likely culprit.
def linreg(X, Y):
if len(X) != len(Y):
raise ValueError('unequal length')
N = len(X)
logging.debug(f'Length of X and Y (N): {N}')
Sx = Sy = Sxx = Syy = Sxy = 0.0
for x, y in zip(X, Y):
Sx += x
Sy += y
Sxx += x * x
Syy += y * y
Sxy += x * y
det = Sxx * N - Sx * Sx
a = (Sxy * N - Sy * Sx) / det
b = (Sxx * Sy - Sx * Sxy) / det
meanerror = residual = 0.0
for x, y in zip(X, Y):
meanerror += (y - Sy / N) ** 2
residual += (y - a * x - b) ** 2
RR = 1 - residual / meanerror
ss = residual / (N - 2)
Var_a = ss * N / det
Var_b = ss * Sxx / det
return a, b, RR
this right?
f-strings don't exist in python2, you'll need to either
- use one of python2 formatting syntaxes
- use the built-in formatted logging functions (see system.util.getLogger | Ignition User Manual)
Note also that to use logging.debug()
, you'll first need to declare and define logging
.
Again, use system.util.getLogger
as explained in the page linked above.
This was not supposed to fix anything, only give you clues about what might be going wrong.
If you read the error message, it is giving you the clues you need to start debugging. What I read is on line 148 in the function linreg(), you have a divide by zero error. I see several places this could be an issue, but there is clearly nothing checking for having a 0 in the denominator of several lines of code. I also cannot pin point the issue as we don't have line numbers, so you should be able to go right to it. So the focus should be on debugging those, or adding code to handle these situations. Look at the "###### Check this line" in the code below.
You can see a situation where det=0
or meanerror=0
or N=2
that would all throw this error.
def linreg(X, Y):
if len(X) != len(Y): raise ValueError, 'unequal length'
N = len(X)
Sx = Sy = Sxx = Syy = Sxy = 0.0
for x, y in map(None, X, Y):
Sx = Sx + x
Sy = Sy + y
Sxx = Sxx + x*x
Syy = Syy + y*y
Sxy = Sxy + x*y
det = Sxx * N - Sx * Sx
a, b = (Sxy * N - Sy * Sx)/det, (Sxx * Sy - Sx * Sxy)/det ###### Check this line
meanerror = residual = float(0.0)
for x, y in map(None, X, Y):
meanerror = meanerror + (y - Sy/N)**2
residual = residual + (y - a * x - b)**2
RR = 1 - residual/meanerror ###### Check this line
ss = residual / (N-2) ###### Check this line
Var_a, Var_b = ss * N / det, ss * Sxx / det ###### Check this line
return a, b, RR #y intercept, slope, risk ratio
While you are at it, I also see a possible error coming if you run this at a certain time (midnight) as well that needs handled appriopriately.
def randomInventoryDecrementation(tagPath):#for simulation
#called from Simulator Trigger tag in UDT
value = system.tag.readBlocking([tagPath])[0].value
hour = system.date.getHour24(system.date.now())
if hour<12:#prevent "running out" every morning
hour = 24-hour
randomNum = 0.500/float(hour) ###### Check this line
newValue = value-randomNum
if newValue<0:
newValue = 100.000
res = system.tag.writeBlocking([tagPath],[newValue])
return
Keep in mind, reusing of code is fine, but the original developer made some assumptions about it's use (when it would be run, like not at midnight, or that certain values would always be present) that you are running into at this point. This would be a good improvement on the code if the original poster could update, but this is what we as developers get to deal with. You can try to detect the 0 before you do the division, or use a try/except here to handle it.