I am having an issue in Asterisk 14.6.1 getting my custom CDR fields populated using ODBC to a MySQL database, basically I am making simultaneous outbound calls using Dial and I need to know in the CDRs which phone actually answered the call, so I am using a macro.
So here’s my simple extensions.conf
exten => 12345,1,Dial(PJSIP/071XXXXXXXX#EP&PJSIP/072XXXXXXXX#EP,180,M(test),r)
And my Macro:
[macro-test]
exten => s,1,Verbose(Call Answered from ${CALLERID(num)} to ${DIALEDPEERNUMBER})
exten => s,2,Set(CDR(outboundddi)=${DIALEDPEERNUMBER})
exten => s,3,Set(CDR(userfield)=${DIALEDPEERNUMBER})
So all this works fine, calls are ringing, being answered and macro running!
Output from Asterisk:
PJSIP/PrimaryEP-00000001 answered PJSIP/EP-00000000
– Executing [s#macro-test:1] Verbose(“PJSIP/EP-00000001”, “Call Answered from 12345 to 071XXXXXXXX#EP”) in new stack
Call Answered from 12345 to 071XXXXXXXX#EP
– Executing [s#macro-test:2] Set(“PJSIP/EP-00000001”, “CDR(outboundddi)= 071XXXXXXXX#EP”) in new stack
– Executing [s#macro-test:3] Set(“PJSIP/EP-00000001”, “CDR(userfield)= 071XXXXXXXX#EP”) in new stack
So I know all this works. The Set(CDR(userfield)=${DIALEDPEERNUMBER}) works fine, the Set(CDR(outboundddi)=${DIALEDPEERNUMBER}) does not, it appears to be ignored, and I do not get an error. So the value is getting populated in the userfield of the database, but nothing gets populated in the outboundddi field of the database.
I think it gets ignored because cdr_odbc is registered in the backend as well as cdr_adaptive_odbc - but if I noload cdr_odbc.so then I do not get any CDRs logged in the ODBC database, and if I take out the config in cdr_odbc.conf then again I get nothing on the ODBC database.
Here is some further detail:
cdr show status
Call Detail Record (CDR) settings
Logging: Enabled
Mode: Simple
Log unanswered calls: Yes
Log congestion: Yes
Registered Backends
cdr-custom
cdr_manager (suspended)
radius
Adaptive ODBC
ODBC
res_config_sqlite
odbc show
ODBC DSN Settings
Name: asterisk
DSN: Asterisk
Number of active connections: 1 (out of 1)
module reload cdr_adaptive_odbc.so
Module ‘cdr_adaptive_odbc.so’ reloaded successfully.
– Reloading module ‘cdr_adaptive_odbc.so’ (Adaptive ODBC CDR backend)
== Parsing ‘/etc/asterisk/cdr_adaptive_odbc.conf’: Found
– Found adaptive CDR table cdrs#asterisk.
> Found calldate column with type 93 with len 19, octetlen 19, and numlen (0,10)
> Found clid column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found src column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found dst column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found dcontext column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found channel column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found dstchannel column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found lastapp column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found lastdata column with type 12 with len 80, octetlen 80, and numlen (0,0)
> Found duration column with type 4 with len 10, octetlen 10, and numlen (0,10)
> Found billsec column with type 4 with len 10, octetlen 10, and numlen (0,10)
> Found disposition column with type 12 with len 45, octetlen 45, and numlen (0,0)
> Found amaflags column with type 4 with len 10, octetlen 10, and numlen (0,10)
> Found accountcode column with type 12 with len 20, octetlen 20, and numlen (0,0)
> Found userfield column with type 12 with len 255, octetlen 255, and numlen (0,0)
> Found uniqueid column with type 12 with len 32, octetlen 32, and numlen (0,0)
> Found linkedid column with type 12 with len 32, octetlen 32, and numlen (0,0)
> Found sequence column with type 12 with len 32, octetlen 32, and numlen (0,0)
> Found peeraccount column with type 12 with len 32, octetlen 32, and numlen (0,0)
> Found outboundddi column with type 12 with len 50, octetlen 50, and numlen (0,0)
cdr_adaptive_cdr.conf
[adaptive_connection]
connection=asterisk
table=cdrs
usegmtime=yes
cdr_odbc.conf
[global]
dsn=asterisk
loguniqueid=yes
dispositionstring=yes
table=cdrs ;“cdr” is default table name
usegmtime=yes ; set to “yes” to log in GMT
hrtime=yes ;Enables microsecond accuracy with the billsec and duration fields
newcdrcolumns=yes ; Enable logging of post-1.8 CDR columns (peeraccount, linkedid, sequence)
I guess I could just forget it and use the userfield that is working, but I’d rather it be stored in a custom CDR field and I’d like to know what is incorrect for my own sanity, so if anyone can point me into what I have configured incorrectly I would be most grateful.
Thanks
David
You have put aliases lines in cdr_adaptive_odbc.conf
alias start => calldate
No, it not scan table itself.
Related
I'm trying to translate an Arduino example project for the M5 Thermal HAT (using MLX90640) (on an M5StickC) which works for me to MicroPython (1.18).
While trying to read data via I2C strange things happen.
Creating the I2C object and scanning the bus with the HAT attached works as expected:
>>> from machine import Pin, I2C
>>> i2c = I2C(0, sda=Pin(0), scl=Pin(26), freq=800000)
>>> i2c.scan()
[51]
Reading the status register at 0x8000 as described in the MLX90680 documentation also returns data which seems plausible to me:
>>> list(i2c.readfrom_mem(0x33, 0x8000, 2))
[0, 8]
>>> list(i2c.readfrom_mem(0x33, 0x8000, 2))
[0, 9]
Bit 3 indicates available data and bit 0 seems to toggle pages.
But now the magic begins.
As I try to read the "control register" at 0x800D as demonstrated in an Adafruit example (and all other examples) it seems to behave exactly like the status register (with a toggling first bit, which should not happen on a control register):
>>> list(i2c.readfrom_mem(0x33, 0x800D, 2))
[0, 9]
>>> list(i2c.readfrom_mem(0x33, 0x800D, 2))
[0, 8]
After a bit of experimenting and playing with different addresses and sizes I had to realize that the address provided with I2C.readfrom_mem() seems to be totally ignored:
>>> list(i2c.readfrom_mem(0x33, 0x0000, 16))
[0, 9, 0, 191, 121, 159, 0, 0, 32, 97, 0, 4, 3, 32, 3, 224]
>>> list(i2c.readfrom_mem(0x33, 0x1000, 16))
[0, 9, 0, 191, 121, 159, 0, 0, 32, 97, 0, 4, 3, 32, 3, 224]
>>> list(i2c.readfrom_mem(0x33, 0x2400, 16))
[0, 9, 0, 191, 121, 159, 0, 0, 32, 97, 0, 4, 3, 32, 3, 224]
>>> list(i2c.readfrom_mem(0x33, 0x2800, 16))
[0, 8, 0, 191, 121, 159, 0, 0, 32, 97, 0, 4, 3, 32, 3, 224]
I tried turning all devices off and on again, played with different values for addrsize and clock speeds and tried a different HAT attached to the stick (to make sure I2C is working fine in principal) but all with no insights.
Since most code examples (e.g. this or that) demonstrating how to use the MLX90640 sensor via I2C use low level access to I2C (rather than MicroPythons readfrom_mem implementation) I also tried a discrete write/read approach:
>>> i2c.writeto(0x33, bytes([0x80, 0x0D]))
2
>>> list(i2c.readfrom(0x33, 2))
[0, 9]
>>> i2c.writeto(0x33, bytes([0x0D, 0x80])) # try swapped byte order
2
>>> list(i2c.readfrom(0x33, 2))
[0, 9]
With all the same effect.
What do I do wrong here? Since I can read data from the I2C bus I guess I don't have a problem of electrical nature.. (also the native Arduino example is fully working on the same device).
Is there some basic I2C knowledge I missed until now?
Why do all I2C read operations on this device issued by MicroPython seem to disregard the address while others are working totally fine?
It looks like you are missing the stop parameter.
micropython I2C writeto
Try this:
i2c.writeto(0x33, bytes([mem_address>>8, mem_address&0xFF]), False)
i2c.readfrom(0x33, 2)
Accordingly to write data you'd write:
i2c.writeto(0x33, bytes([mem_address>>8, mem_address&0xFF] + [d,a,t,a]))
I have a set of data (cumulative precipitation) that when the instrument resets, the starting level of the precipitation recorded changes. To correct this, I need to add the amount by which the the value dropped to the new recording so that the values are continuously increasing as they should.
i.e. the range of numbers below would be changed from:
26, 27, 28, 18, 19, 20
to:
26, 27, 28, 28, 29, 30
By adding the drop (10) to all values after the drop.
I think I need to loop the action through a range of cells (12746 to 17567)
You could do something like this:
# An example like yours, but with multiple drops.
vals = c(26, 27, 28, 18, 19, 20, 15, 17, 19);
correct_drops = function(vals)
{
n = length(vals);
# which() will get you indices where the condition holds true.
# Here it will get you the sites where 2 consecutive values
# have that the first value is greater than the second, meaning a drop.
indices_before_drop = which(vals[1:(n-1)] - vals[2:n] > 0);
# Looping through all drops, correcting each individually.
vals_corrected = vals;
for (ibd in indices_before_drop)
{
iad = ibd + 1;
drop = vals_corrected[ibd] - vals_corrected[iad];
vals_corrected = c(vals_corrected[1:ibd], (vals_corrected[iad:n] + drop));
}
return (vals_corrected);
}
vals_corrected = correct_drops(vals);
Of course there are special cases to be considered.
In any case I believe that which(..) and c(..) and subvector ranges v[a:b] are your friends here.
I know that Javascript Date() function can handle date entry overflows. But in Julia I get Error.
Is there any way to handle overflows automatically?
DateTime(2020, 4, 22, 15, 43, 67) # ----> 2020-4-22T15:44:07
DateTime(2020, 12, 31, 23, 59, 60) # ----> 2021-1-1T00:00:00
I find the default behavior of throwing an error useful. If you want to allow overflows you can define your own function for this eg. like this:
julia> MyDateTime(y, m, d, h, mi, s) =
+(DateTime(0), Year(y), Month(m-1), Day(d-1),
Hour(h), Minute(mi), Second(s))
MyDateTime (generic function with 1 method)
julia> MyDateTime(2020, 4, 22, 15, 43, 67) # ----> 2020-4-22T15:44:07
2020-04-22T15:44:07
julia> MyDateTime(2020, 12, 31, 23, 59, 60) # ----> 2021-1-1T00:00:00
2021-01-01T00:00:00
Note that the order of operations matters there - we first advance year, then month, etc. (as e.g. the effect of advancing time by one second may depend on the month, year and day):
julia> MyDateTime(2020, 2, 28, 23, 59, 60)
2020-02-29T00:00:00
julia> MyDateTime(2021, 2, 28, 23, 59, 60)
2021-03-01T00:00:00
(this can get especially tricky if you have very large and invalid values of month, day etc.)
I have this map
%{
total: 38,
value: 22
}
And would like to add the key :ratio. Is there a way to write immediately:
%{
total: 38,
value: 22,
ratio: __SELF__.value / __SELF__.total
}
or do I need to create another map to achieve this?
Thanks
All data is immutable, so you always have to make a new map.
A simple way, assuming your map is called map:
iex> Map.put(map, :ratio, map.value / map.total)
%{ratio: 0.5789473684210527, total: 38, value: 22}
If you mean that you want to create the map before it already exists, then it would be better to put total and value into variables, and use them to build the map:
defmodule Example do
def make_map(total, value) do
%{total: total, value: value, ratio: value / total}
end
end
iex(1)> Example.make_map(38, 22)
%{ratio: 0.5789473684210527, total: 38, value: 22}
I really wanted to avoid assigning the temporary map to a variable.
Well, because the data at a specific memory location is immutable, elixir can safely use pointers to those memory locations inside new collections. Therefore, creating a temporary variable with intermediate results does not double the amount of memory used. For instance, if you begin with a map that has 1 million entries and you create a new map with one additional entry, you do not use total memory of:
old_map new_map
| |
V V
1 million + ( 1 million + 1)
Rather you only use additional memory of:
new_entry
|
V
1 + pointer_to_old_map
...plus a little extra for the new map's bookkeeping. Like this:
old_map
^
|
new_map = %{ +, a: 10}
Is there a way to write immediately:
%{
total: 38,
value: 22,
ratio: __SELF__.value / __SELF__.total
}
Yes:
%{
total: 38,
value: 22,
ratio: 22/38
}
Now, if you have a list of maps to which you want to add a ratio key:
data = [
%{total: 38, value: 22},
%{total: 40, value: 22},
%{total: 44, value: 22}
]
for %{total: t, value: v}=map <- data do
Map.put(map, :ratio, v/t)
end
output:
[
%{ratio: 0.5789473684210527, total: 38, value: 22},
%{ratio: 0.55, total: 40, value: 22},
%{ratio: 0.5, total: 44, value: 22}
]
After each iteration of the for comprehension, the memory locations of t and v are immediately subject to garbage collection and a pointer to the memory location of map is used in the new map.
The following go code doesn't compile, because (I believe) there is a mistake around the way pointers are being referenced.
In particular, The error message is
prog.go:13: cannot use append((*x)[:remove], (*x)[remove + 1:]...) (type []int) as type *[]int in assignment
Here is an abstracted and simplified version of the code which results in this error message.
package main
import "fmt"
func main() {
x := &[]int{11, 22, 33, 44, 55, 66, 77, 88, 99}
for i, addr := range *x {
if addr == 22 {
for len(*x) > 5 {
remove := (i + 1) % len(*x)
x = append((*x)[:remove], (*x)[remove+1:]...)
}
break
}
}
fmt.Println(x)
}
You're not using an array here, you're using a slice. Generally, you don't want to handle a pointer to a slice since it can get awkward, and the pointer is needed in very few cases.
To fix your error, dereference x:
*x = append((*x)[:remove], (*x)[remove+1:]...)
But you should probably be using the slice value directly, so that no dereferences are required:
x := []int{11, 22, 33, 44, 55, 66, 77, 88, 99}