Noticias:

Grupo en telegram, del foro de meteorología fácil: https://t.me/meteorologiafacil

Para mas detalles, puedes visitar el siguiente tema http://www.meteorologiafacil.com.ar/foros/index.php?topic=1608.0

Espero que les sea de mucha utilidad.

Menú Principal

Función count

Iniciado por David Met, Junio 19, 2015, 12:21:10 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

David Met

Hola a todos.
A partir de una  librería de string en Librerías para JAL, y con el ejemplo que me dio NESTORIANO sobre una consulta de buscar una frase dentro de un string (buscar un string dentro de otro string), intenté hacer una función general; ya que mi ejemplo y el de Nestoriano, es sobre una frase en particular; cuando en realidad, el string que voy a recibir, cambia constantemente.

Viendo las funciones que hay en dicha  librería llamada jtring.jal, vi que utilizan una función que está integrada en el "motor" de jal, y es count. La misma cuenta los caracteres que hay dentro de un string (cadena); o al menos, es es lo que entendí.

Por lo tanto, la función que escribí devuelve 1, si se encontró la frase o palabra que se busca, o de lo contrario devuelve 0.
La estrategia fue:

1) Contar los carateres que hay en la cadena recibida (se llama cadena) para saber hasta donde hay que buscar.
2) Contar los caracteres que hay en la cadena frase (se llama frase) que, justamente es lo que queremos buscar dentro del string cadena[]. Se cuenta por el mismo tema anterior.
3) Comparar caracter por caracter. Si hay coincidencia, sumamos 1 a la variable coincidencia. De lo contrario lo ponemos a 0.
4) Ir comparando el valor de coincidencia con el tamaño o cantidad de la cadena frase[]. Si coinciden, ya encontramos la frase; de lo contrario seguimos buscando.
5) Al mismo tiempo, ver que no excedamos la cantidad cadena[]. Si al cazamos ese valor y coincidencia no tiene el mismo tamaño de frase, quiere decir que no lo encontramos y devuelve 0

-- ----------------------------------------------------------------------------
-- Encuentra una palabra o frase dentro de una cadena.
-- ----------------------------------------------------------------------------
function EncPalCad (byte in cadena[], byte in frase[]) return bit is

var byte i
var byte x
var byte pos_cadena = 0
var byte pos_frase = 0
var byte control
var byte coincidencia
i = StrLen (cadena[])
x = count (frase[])
control = 0

while control != i loop
if control == i then
if coincidencia == x then
return true
else if
return false
end if
else if
if cadena[pos_cadena] == frase[pos_frase]
coincidencia = coincidencia + 1
if coincidencia == x then
return true
else if
pos_cadena = pos_cadena + 1
pos_frase = pos_frase + 1
control = control + 1
end if
else if
coincidencia = 0
control = control + 1
pos_frase = 0
pos_cadena = pos_cadena + 1
end if
end if
end loop
end function


El problema es que me tira los siguiente errores:

generating p-code
basico.jal:168: constant expression expected
basico.jal:168: warning: assignment to smaller type; truncation possible
basico.jal:169: constant expression expected
basico.jal:169: warning: assignment to smaller type; truncation possible
basico.jal:176: warning: boolean expression expected
basico.jal:176: 'then' expected (got 'return')
basico.jal:184: warning: boolean expression expected
basico.jal:184: 'then' expected (got '=')
basico.jal:216: constant expression expected
basico.jal:216: constant expression expected
basico.jal:218: 'then' expected (got '=')
basico.jal:221: 'end' expected (got '')
basico.jal:221: {IF starts at basico.jal:218}
basico.jal:221: 'end' expected (got '')
basico.jal:221: {IF starts at basico.jal:216}
basico.jal:221: 'end' expected (got '')
basico.jal:221: {IF starts at basico.jal:176}
basico.jal:221: 'end' expected (got '')
basico.jal:221: {IF starts at basico.jal:174}
basico.jal:221: 'end' expected (got '')
basico.jal:221: {IF starts at basico.jal:173}
basico.jal:221: 'end' expected (got '')
basico.jal:221: {WHILE starts at basico.jal:172}
basico.jal:221: 'end' expected (got '')
basico.jal:221: {function starts at basico.jal:160}
21 errors, 4 warnings


El que más me preocupa es 168 y 169 que corresponde a:

        i = count (cadena[])
   x = count (frase[])

¿Alguien sabe a que se debe eso, o como solucionarlo?
Jesús dijo, yo soy el CAMINO, la VERDAD y la VIDA, nadie llega al PADRE si no es por mi.

YO

Hola David!

Probá así:

var byte i = StrLen (cadena[])
var byte x = count (frase[]) 

Lo que te tira como mensaje es igual a cuando no tenés determinada la variable, y sería conveniente que la declares fuera de la función (como variable general) para que la tome de cualquier lado.

Espero te sirva 
Sin saber que era imposible El fue y lo hizo

David Met

Lamentablemente, mismo error; tanto como tu ejemplo como declararlas fuera de la función. >:[[
Jesús dijo, yo soy el CAMINO, la VERDAD y la VIDA, nadie llega al PADRE si no es por mi.

YO

#3
Sería más fácil ayudarte si estuviese el código completo, incluso lo podría compilar desde acá y ver que onda,  pero como los errores o precauciones te los tira por número de línea de programa y esos números no aparecen en el código, estoy desubicado sin saber por donde buscar, por ejemplo:

basico.jal:168: constant expression expected 
Cuando me pasaba esto era porque no tenía declarada la constante que usaba en algún punto del programa.

basico.jal:168: warning: assignment to smaller type; truncation possible
Esto era cuando le asignaba a una variable o constante un valor chico Ej: ponía byte y debía ser word.

basico.jal:176: warning: boolean expression expected
Acá era cuando quería hacer una cuenta y no tenía bien los datos de como lo quería hacer.
Muchas veces corrigiendo algún error o warning citado antes, el resto se acomodaba solo y tenía sentido el resto, entonces dejaba de haber más errores.

basico.jal:184: 'then' expected (got '=')
Ésto quiere decir que te comiste otro = debió ser == Ej.: else if coincidencia = 0 (te faltó ponerle otro = Debió ser  else if coincidencia == 0

Acá me parece que tenes otro error:
else if
         if cadena[pos_cadena] == frase[pos_frase]

Si pongo else if , no le gusta porque sería else o elsif nunca me deja else if , lo toma como que a ese if suelto le falta la condición, por lo tanto todo lo que vá después no tiene sentido para el compilador.
Fijate que hasta te cuestiona el end final porque tenes algo que te quedó sin resolver (abierto)
Tal vez mejore con :
                                   elsif  cadena[pos_cadena] == frase[pos_frase]  then

o tal vez asi :   else  cadena[pos_cadena] = frase[pos_frase]

Ojalá te sirva




            
Sin saber que era imposible El fue y lo hizo

David Met

¿Sabes que no me había percatado de esos else if y luego if nuevamente? Ya los corregí y decayeron muchísimos los errores.

Aquí va el código completo:

-- ------------------------------------------------------------------------------------------------
-- ------------------------------------------------------------------------------------------------
-- Servidor web básico.
-- ------------------------------------------------------------------------------------------------

-- ------------------------------------------------------------------------------------------------
-- Creado por Leo Persi
--
-- Iniciado el 12 de junio de 2.015
--
-- ------------------------------------------------------------------------------------------------
-- TÍTULO: Servidor web básico.
-- VERSIÓN: 1.0.0.
-- AUTOR: Leo D. Persi.
-- PROGRAMA PARA PIC: Leo D. Persi.
-- PROGRAMA PARA PC:
-- FECHA DE LA ÚLTIMA ACTUALIZACIÓN: 12 de junio de 2.015.
--
-- DESCRIPCIÓN:
-- ------------
-- El presente programa, realiza un servidor web básico para ser usado con el módulo
-- ESP8266.
--
-- ------------------------------------------------------------------------------------------------
--
-- Este programa ha sido generado por jallib.py
-- Plataforma entrenadora Felixls.
--
-- ------------------------------------------------------------------------------------------------
-- INICIO DEL PROGRAMA.
-- --------------------

include 18f4550 -- Incluímos la librería para controlar nuestro PIC.

-- Aunque el cristal externo es 20 MHz, la configuración es tal que el reloj de la CPU
-- se deriva de reloj PLL 96 Mhz (div2),
-- por lo tanto, establecer la frecuencia de destino para 48 MHz
pragma target clock       48_000_000


-- FUSIBLES:
-- ---------
pragma target PLLDIV        P5          -- divide by 5 - 20MHZ_INPUT
pragma target CPUDIV        P1          -- [primary oscillator src: /1][96 mhz pll src: /2]
pragma target USBPLL        F48MHZ      -- CLOCK_SRC_FROM_96MHZ_PLL_2
pragma target OSC           HS_PLL
pragma target FCMEN         DISABLED
pragma target IESO          DISABLED
pragma target PWRTE         DISABLED    -- power up timer
pragma target VREGEN        ENABLED     -- USB voltage regulator
pragma target VOLTAGE       MINIMUM     -- brown out voltage
pragma target BROWNOUT      DISABLED    -- no brownout detection
pragma target WDTPS         P32K        -- watch dog saler setting
pragma target WDT           CONTROL     -- no watchdog
pragma target CCP2MUX       ENABLED    -- CCP2 pin C1
pragma target PBADEN        DIGITAL     -- digital input port<0..4>
pragma target LPT1OSC       LOW_POWER   -- low power timer 1
pragma target MCLR          EXTERNAL    -- master reset on RE3
pragma target STVR          DISABLED    -- reset on stack over/under flow
pragma target LVP           DISABLED    -- no low-voltage programming
pragma target XINST         ENABLED     -- extended instruction set
pragma target DEBUG         DISABLED    -- background debugging
pragma target CP0           DISABLED    -- code block 0 not protected
pragma target CP1           DISABLED    -- code block 1 not protected
pragma target CP2           DISABLED    -- code block 2 not protected
pragma target CP3           DISABLED    -- code block 3 not protected
pragma target CPB           DISABLED    -- bootblock code not write protected
pragma target CPD           DISABLED    -- eeprom code not write protected
pragma target WRT0          DISABLED    -- table writeblock 0 not protected
pragma target WRT1          DISABLED    -- table write block 1 not protected
pragma target WRT2          DISABLED    -- table write block 2 not protected
pragma target WRT3          DISABLED    -- table write block 3 not protected
pragma target WRTB          DISABLED    -- bootblock not write protected
pragma target WRTD          DISABLED    -- eeprom not write protected
pragma target WRTC          DISABLED    -- config not write protected
pragma target EBTR0         DISABLED    -- table read block 0 not protected
pragma target EBTR1         DISABLED    -- table read block 1 not protected
pragma target EBTR2         DISABLED    -- table read block 2 not protected
pragma target EBTR3         DISABLED    -- table read block 3 not protected
pragma target EBTRB         DISABLED    -- boot block not protected
-- ------------------------------------------------------------------------------------------------
-- CONFIGURACIÓN GENERAL DEL PIC:
-- ------------------------------
--
-- ------------------------------------------------------------------------------------------------
var volatile bit usb_en_uso = 0 -- Ver en la descripción de la interrupción.
var volatile bit usb_iniciado = 0
include usb_serial -- Incluímos la librería para emular el puerto serial por USB.
usb_iniciado = 0
usb_serial_init()
usb_iniciado = 1
-- ------------------------------------------------------------------------------------------------
-- VARIABLES:
-- -----------
var byte ch -- Recupera el valor recibido por USB
var byte i
var byte x
-- ------------------------------------------------------------------------------------------------
-- INCLUIMOS LIBRERÍAS:
-- --------------------
include print
;include jstring

-- ------------------------------------------------------------------------------------------------
-- CONFIGURACIÓN DEL TMR0:
-- -----------------------
INTCON=0b10100000
T0CON=0b10000000
tmr0=64000
-- ------------------------------------------------------------------------------------------------
-- Iniciamos la conexión USB y el adc.
-- -----------------------------------

-- ------------------------------------------------------------------------------------------------
-- TRATAMIENTO DE LAS INTERRUPCIONES:
-- ----------------------------------
-- Descripción:
-- ------------
-- La interrupción ocurre cada 250useg. En la interrupción, se analiza el estado del bit usb_en_uso
-- para saber si hay que mantener viva la conexión USB o no.
-- Si usb_en_uso es 1; indica que se está transmitiendo datos por USB y no es necesario mantener viva
-- la conexión. Por lo tanto no se ejecuta la función usb_serial_flush().
-- En caso de que el bit usb_en_uso valga 0; quiere decir que no se está transmitiendo ningún dato,
-- por lo tanto, es necesario mantener viva la conexión y se tendrá que ejecutar la funsión usb_serial_flush()
-- ----------------------------------
procedure interrupcion() is
pragma interrupt
if (INTCON_TMR0IF) then
if usb_iniciado == 1 then
if usb_en_uso == 0 then
usb_serial_flush()
end if
end if
tmr0=64000
INTCON_TMR0IF=0
end if
end procedure
-- ------------------------------------------------------------------------------------------------
-- PROCEDIMIENTOS Y FUNCIONES TOTALES.
-- -----------------------------------
--
--
const        byte STRING_MAX           = 255

function StrLen (byte in str[]) return byte is

   var byte i
   for STRING_MAX using i loop
       if str[i] == 0 then
          return i
       end if
   end loop
   return STRING_MAX

end function


-- ----------------------------------------------------------------------------
-- Encuentra una palabra o frase dentro de una cadena.
-- ----------------------------------------------------------------------------
function EncPalCad (byte in cadena[], byte in frase[]) return bit is

var byte pos_cadena = 0
var byte pos_frase = 0
var byte control
var byte coincidencia
i = count (cadena[])
x = count (frase[])
control = 0

while control != i loop
if control == i then
if coincidencia == x then
return true
else
return false
end if
else
if cadena[pos_cadena] == frase[pos_frase] then
coincidencia = coincidencia + 1
if coincidencia == x then
return true
else
pos_cadena = pos_cadena + 1
pos_frase = pos_frase + 1
control = control + 1
end if
else
coincidencia = 0
control = control + 1
pos_frase = 0
pos_cadena = pos_cadena + 1
end if
end if
end loop
end function

-- ------------------------------------------------------------------------------------------------
-- MENU PRINCIPAL.
-- ---------------

var byte cadena[] = "MI MAMA ME MIMA"
var byte frase[] = "MAMA"

forever loop
;
usb_en_uso = 0 -- Indicamos que no vamos a transmitir por USB.


if usb_serial_read(ch) then
usb_en_uso = 0

end if

if EncPalCad(cadena[],frase[])== 1 then
usb_en_uso = 1
else
usb_en_uso = 0
end if
end loop


Lo que está en for ever loop, realmente no me interesa mucho. Mi idea era simularlo y ver los estados de las memorias RAM para ver como se comportaba. Luego me iba a disponer de hacerlo funcionar en el PIC conectado al PC por USB.
A mi me desconcierta que espera una constante. Si la declaro como constante, también me tira error.
Jesús dijo, yo soy el CAMINO, la VERDAD y la VIDA, nadie llega al PADRE si no es por mi.

David Met

#5
Bueno, lo acabo de hacer andar.
Era lo más evidente. Lo estaba usando mal. La función count, es solo para constantes. >:[|

Para ello, utilicé una función que trae la librería jstring.jal que busca un caracter. No salió andando de una. Tuve el problema de que contaba mal la cantidad de caracteres, y se debía que la función que cuenta la cantidad de caracteres en una cadena, contaba hasta encontrar un 0 al final de la cadena. Es por ello que le tuve que agregar, al final de la cadena \x00 que significa un valor en hexadecimal, que en este caso es 0

-- ------------------------------------------------------------------------------------------------
-- ------------------------------------------------------------------------------------------------
-- Servidor web básico.
-- ------------------------------------------------------------------------------------------------

-- ------------------------------------------------------------------------------------------------
-- Creado por Leo Persi
--
-- Iniciado el 12 de junio de 2.015
--
-- ------------------------------------------------------------------------------------------------
-- TÍTULO: Servidor web básico.
-- VERSIÓN: 1.0.0.
-- AUTOR: Leo D. Persi.
-- PROGRAMA PARA PIC: Leo D. Persi.
-- PROGRAMA PARA PC:
-- FECHA DE LA ÚLTIMA ACTUALIZACIÓN: 12 de junio de 2.015.
--
-- DESCRIPCIÓN:
-- ------------
-- El presente programa, realiza un servidor web básico para ser usado con el módulo
-- ESP8266.
--
-- ------------------------------------------------------------------------------------------------
--
-- Este programa ha sido generado por jallib.py
-- Plataforma entrenadora Felixls.
--
-- ------------------------------------------------------------------------------------------------
-- INICIO DEL PROGRAMA.
-- --------------------

include 18f4550 -- Incluímos la librería para controlar nuestro PIC.

-- Aunque el cristal externo es 20 MHz, la configuración es tal que el reloj de la CPU
-- se deriva de reloj PLL 96 Mhz (div2),
-- por lo tanto, establecer la frecuencia de destino para 48 MHz
pragma target clock       48_000_000


-- FUSIBLES:
-- ---------
pragma target PLLDIV        P5          -- divide by 5 - 20MHZ_INPUT
pragma target CPUDIV        P1          -- [primary oscillator src: /1][96 mhz pll src: /2]
pragma target USBPLL        F48MHZ      -- CLOCK_SRC_FROM_96MHZ_PLL_2
pragma target OSC           HS_PLL
pragma target FCMEN         DISABLED
pragma target IESO          DISABLED
pragma target PWRTE         DISABLED    -- power up timer
pragma target VREGEN        ENABLED     -- USB voltage regulator
pragma target VOLTAGE       MINIMUM     -- brown out voltage
pragma target BROWNOUT      DISABLED    -- no brownout detection
pragma target WDTPS         P32K        -- watch dog saler setting
pragma target WDT           CONTROL     -- no watchdog
pragma target CCP2MUX       ENABLED    -- CCP2 pin C1
pragma target PBADEN        DIGITAL     -- digital input port<0..4>
pragma target LPT1OSC       LOW_POWER   -- low power timer 1
pragma target MCLR          EXTERNAL    -- master reset on RE3
pragma target STVR          DISABLED    -- reset on stack over/under flow
pragma target LVP           DISABLED    -- no low-voltage programming
pragma target XINST         ENABLED     -- extended instruction set
pragma target DEBUG         DISABLED    -- background debugging
pragma target CP0           DISABLED    -- code block 0 not protected
pragma target CP1           DISABLED    -- code block 1 not protected
pragma target CP2           DISABLED    -- code block 2 not protected
pragma target CP3           DISABLED    -- code block 3 not protected
pragma target CPB           DISABLED    -- bootblock code not write protected
pragma target CPD           DISABLED    -- eeprom code not write protected
pragma target WRT0          DISABLED    -- table writeblock 0 not protected
pragma target WRT1          DISABLED    -- table write block 1 not protected
pragma target WRT2          DISABLED    -- table write block 2 not protected
pragma target WRT3          DISABLED    -- table write block 3 not protected
pragma target WRTB          DISABLED    -- bootblock not write protected
pragma target WRTD          DISABLED    -- eeprom not write protected
pragma target WRTC          DISABLED    -- config not write protected
pragma target EBTR0         DISABLED    -- table read block 0 not protected
pragma target EBTR1         DISABLED    -- table read block 1 not protected
pragma target EBTR2         DISABLED    -- table read block 2 not protected
pragma target EBTR3         DISABLED    -- table read block 3 not protected
pragma target EBTRB         DISABLED    -- boot block not protected
-- ------------------------------------------------------------------------------------------------
-- CONFIGURACIÓN GENERAL DEL PIC:
-- ------------------------------
--
-- ------------------------------------------------------------------------------------------------
var volatile bit usb_en_uso = 0 -- Ver en la descripción de la interrupción.
var volatile bit usb_iniciado = 0
include usb_serial -- Incluímos la librería para emular el puerto serial por USB.
usb_iniciado = 0
usb_serial_init()
usb_iniciado = 1
-- ------------------------------------------------------------------------------------------------
-- VARIABLES:
-- -----------
var byte ch -- Recupera el valor recibido por USB
var byte i
var byte x
-- ------------------------------------------------------------------------------------------------
-- INCLUIMOS LIBRERÍAS:
-- --------------------
include print
;include jstring

-- ------------------------------------------------------------------------------------------------
-- CONFIGURACIÓN DEL TMR0:
-- -----------------------
INTCON=0b10100000
T0CON=0b10000000
tmr0=64000
-- ------------------------------------------------------------------------------------------------
-- Iniciamos la conexión USB y el adc.
-- -----------------------------------

-- ------------------------------------------------------------------------------------------------
-- TRATAMIENTO DE LAS INTERRUPCIONES:
-- ----------------------------------
-- Descripción:
-- ------------
-- La interrupción ocurre cada 250useg. En la interrupción, se analiza el estado del bit usb_en_uso
-- para saber si hay que mantener viva la conexión USB o no.
-- Si usb_en_uso es 1; indica que se está transmitiendo datos por USB y no es necesario mantener viva
-- la conexión. Por lo tanto no se ejecuta la función usb_serial_flush().
-- En caso de que el bit usb_en_uso valga 0; quiere decir que no se está transmitiendo ningún dato,
-- por lo tanto, es necesario mantener viva la conexión y se tendrá que ejecutar la funsión usb_serial_flush()
-- ----------------------------------
procedure interrupcion() is
pragma interrupt
if (INTCON_TMR0IF) then
if usb_iniciado == 1 then
if usb_en_uso == 0 then
usb_serial_flush()
end if
end if
tmr0=64000
INTCON_TMR0IF=0
end if
end procedure
-- ------------------------------------------------------------------------------------------------
-- PROCEDIMIENTOS Y FUNCIONES TOTALES.
-- -----------------------------------
--
--
const        byte STRING_MAX           = 255

function StrLen (byte in str[]) return byte is

   var byte i
   for STRING_MAX using i loop
       if str[i] == 0 then
          return i
       end if
   end loop
   return STRING_MAX

end function

-- ----------------------------------------------------------------------------
-- Encuentra una palabra o frase dentro de una cadena.
-- ----------------------------------------------------------------------------
function EncPalCad (byte in cadena[], byte in frase[]) return bit is

var byte pos_cadena = 0
var byte pos_frase = 0
var word control = 0
var byte coincidencia = 0
var word i = StrLen (cadena)
var word x = StrLen (frase)

while control != i loop
if control == i then
if coincidencia == x then
return true
else
return false
end if
else
if cadena[pos_cadena] == frase[pos_frase] then
coincidencia = coincidencia + 1
if coincidencia == x then
return true
else
pos_cadena = pos_cadena + 1
pos_frase = pos_frase + 1
control = control + 1
end if
else
coincidencia = 0
control = control + 1
pos_frase = 0
pos_cadena = pos_cadena + 1
end if
end if
end loop
end function

-- ------------------------------------------------------------------------------------------------
-- MENU PRINCIPAL.
-- ---------------

var byte cadena[] = "MI MAMA ME MIMA\x00"
var byte frase[] = "MAMA\x00"

forever loop
;
usb_en_uso = 0 -- Indicamos que no vamos a transmitir por USB.


if usb_serial_read(ch) then
usb_en_uso = 0

end if

if EncPalCad(cadena,frase) == 1 then
usb_en_uso = 1
else
usb_en_uso = 0
end if
end loop


Siguiente paso, encontrar la manera de agregar, al final de la cadena, el valor 0. Me refiero a que yo voy a recibir una cadena, y al final no va a tener un valor 0. Por ahora tengo que analizar como me llega la cadena, ya que sospecho que me llega con \n y/o con \r
Jesús dijo, yo soy el CAMINO, la VERDAD y la VIDA, nadie llega al PADRE si no es por mi.

nestoriano

Hola.

No conocía el modulo 8266 y parece bastante interesante, pero un lío procesar los datos.

Desconozco el código de la parte de recepción datos, pero si necesitas agregar un 0 al final de la cadena lo que se me ocurre es inicializar todos datos con ceros antes de comenzar a recibir.

Los datos van reemplazando los ceros a medida que vayan entrando.


Si lo que necesitas es una comunicación inalámbrica entre PICs, yo te recomendaría los módulos NRF24L01+ y te olvidas de los string.
Podes mandar máximo de a 32 bytes pero te los manda a 2Mbit/s y cuestan casi lo mismo que el par de módulos 433.

Saludos.

David Met

Si, son excelentes estos módulos pero complicado de hacerlos andar.
Ahora lo dejé pro falta de tiempo. De echo hasta cree una función para buscar cadena dentro de una cadena; pero está en versión beta. Aún no lo probé como es debido.

Lamentablemente JAL me está desilucionando, y seguramente iré a otro lenguaje.
Jesús dijo, yo soy el CAMINO, la VERDAD y la VIDA, nadie llega al PADRE si no es por mi.