2019-03-05 12:35:09 -07:00
defmodule FarmbotOS.Platform.Target.Network do
2018-11-28 13:12:25 -07:00
@moduledoc " Manages Network Connections "
2019-12-17 15:12:25 -07:00
2019-06-25 16:28:08 -06:00
use GenServer , shutdown : 10_000
2019-07-09 10:39:06 -06:00
require Logger
2019-06-25 16:28:08 -06:00
require FarmbotCore.Logger
2019-10-24 11:37:17 -06:00
require FarmbotTelemetry
2019-08-09 11:32:30 -06:00
import FarmbotOS.Platform.Target.Network.Utils ,
only : [
maybe_hack_tzdata : 0 ,
init_net_kernel : 0 ,
build_hostap_ssid : 0
]
alias FarmbotOS.Platform.Target.Network.PreSetup
2019-06-25 16:28:08 -06:00
alias FarmbotOS.Platform.Target.Configurator . { Validator , CaptivePortal }
2019-07-23 11:34:25 -06:00
alias FarmbotCore . { Asset , Config , Leds }
2019-06-25 16:28:08 -06:00
@default_network_not_found_timer_minutes 20
def host do
%{
type : CaptivePortal ,
2019-12-16 11:20:26 -07:00
vintage_net_wifi : %{
2019-10-29 09:59:20 -06:00
networks : [
%{
ssid : build_hostap_ssid ( ) ,
mode : :ap ,
key_mgmt : :none
}
]
2019-06-25 16:28:08 -06:00
} ,
ipv4 : %{
method : :static ,
address : " 192.168.24.1 " ,
netmask : " 255.255.255.0 "
} ,
2020-03-28 00:08:26 -06:00
dhcpd : %{
2019-06-25 16:28:08 -06:00
start : " 192.168.24.2 " ,
end : " 192.168.24.10 "
}
}
end
def null do
%{ type : VintageNet.Technology.Null }
end
2019-07-01 12:16:01 -06:00
def presetup do
%{ type : PreSetup }
end
2019-06-25 16:28:08 -06:00
def is_first_connect? ( ) do
# email = Config.get_config_value(:string, "authorization", "email")
# password = Config.get_config_value(:string, "authorization", "password")
# server = Config.get_config_value(:string, "authorization", "server")
token = Config . get_config_value ( :string , " authorization " , " token " )
is_nil ( token )
end
2018-11-28 13:12:25 -07:00
def start_link ( args ) do
2019-06-25 16:28:08 -06:00
GenServer . start_link ( __MODULE__ , args , name : __MODULE__ )
2018-11-28 13:12:25 -07:00
end
2019-06-18 08:49:51 -06:00
@impl GenServer
def init ( _args ) do
2019-06-25 16:28:08 -06:00
_ = maybe_hack_tzdata ( )
2019-08-09 11:32:30 -06:00
_ = init_net_kernel ( )
2019-06-19 15:16:39 -06:00
send ( self ( ) , :setup )
2020-02-11 14:04:31 -07:00
# If a secret exists, assume that
2019-06-25 16:28:08 -06:00
# farmbot at one point has been connected to the internet
first_connect? = is_first_connect? ( )
if first_connect? do
2019-07-23 11:34:25 -06:00
_ = Leds . blue ( :slow_blink )
2019-06-25 16:28:08 -06:00
:ok = VintageNet . configure ( " wlan0 " , null ( ) )
Process . sleep ( 1500 )
:ok = VintageNet . configure ( " wlan0 " , host ( ) )
end
2019-08-09 11:32:30 -06:00
{ :ok , %{ network_not_found_timer : nil , first_connect? : first_connect? } }
2019-06-25 16:28:08 -06:00
end
@impl GenServer
def terminate ( _ , _ ) do
:ok = VintageNet . configure ( " wlan0 " , null ( ) )
:ok = VintageNet . configure ( " eth0 " , null ( ) )
2018-11-28 13:12:25 -07:00
end
2019-06-19 15:16:39 -06:00
@impl GenServer
def handle_info ( :setup , state ) do
2019-06-25 16:28:08 -06:00
configs = Config . get_all_network_configs ( )
case configs do
[ ] ->
Process . send_after ( self ( ) , :setup , 5_000 )
{ :noreply , state }
_ ->
2019-07-01 12:16:01 -06:00
_ = VintageNet . subscribe ( [ " interface " , " wlan0 " ] )
_ = VintageNet . subscribe ( [ " interface " , " eth0 " ] )
:ok = VintageNet . configure ( " wlan0 " , presetup ( ) )
:ok = VintageNet . configure ( " eth0 " , presetup ( ) )
2019-06-25 16:28:08 -06:00
Process . sleep ( 1500 )
2019-07-01 12:16:01 -06:00
{ :noreply , state }
end
end
2020-01-17 08:58:53 -07:00
def handle_info (
{ VintageNet , [ " interface " , ifname , " type " ] , _old , type , _meta } ,
state
)
2019-07-01 12:16:01 -06:00
when type in [ PreSetup , VintageNet.Technology.Null ] do
2020-01-17 08:58:53 -07:00
FarmbotCore.Logger . debug (
1 ,
" Network interface needs configuration: #{ ifname } "
)
2019-07-01 12:16:01 -06:00
case Config . get_network_config ( ifname ) do
% Config.NetworkInterface { } = config ->
FarmbotCore.Logger . busy ( 3 , " Setting up network interface: #{ ifname } " )
2019-07-19 09:08:13 -06:00
case reset_ntp ( ) do
:ok ->
:ok
error ->
FarmbotCore.Logger . error ( 1 , """
Failed to configure NTP : #{inspect(error)}
""" )
end
2019-07-01 12:16:01 -06:00
vintage_net_config = to_vintage_net ( config )
2020-02-11 14:04:31 -07:00
FarmbotCore.Logger . info ( 3 , inspect ( vintage_net_config ) )
2020-01-17 08:58:53 -07:00
2020-02-11 15:25:32 -07:00
FarmbotTelemetry . event ( :network , :interface_configure , nil ,
interface : ifname
)
2020-01-17 08:58:53 -07:00
2019-07-01 12:16:01 -06:00
configure_result = VintageNet . configure ( config . name , vintage_net_config )
2020-01-17 08:58:53 -07:00
FarmbotCore.Logger . success (
3 ,
" #{ config . name } setup: #{ inspect ( configure_result ) } "
)
2019-07-01 12:16:01 -06:00
state = start_network_not_found_timer ( state )
2019-08-09 11:32:30 -06:00
{ :noreply , state }
2019-07-01 12:16:01 -06:00
nil ->
{ :noreply , state }
2019-06-19 15:16:39 -06:00
end
end
2019-06-25 16:28:08 -06:00
2020-01-17 08:58:53 -07:00
def handle_info (
{ VintageNet , [ " interface " , ifname , " lower_up " ] , _old , false , _meta } ,
state
) do
FarmbotCore.Logger . error (
1 ,
" Interface #{ ifname } disconnected from access point "
)
2020-02-11 15:25:32 -07:00
FarmbotTelemetry . event ( :network , :interface_disconnect , nil ,
interface : ifname
)
2020-01-17 08:58:53 -07:00
2019-06-25 16:28:08 -06:00
state = start_network_not_found_timer ( state )
{ :noreply , state }
end
2020-01-17 08:58:53 -07:00
def handle_info (
{ VintageNet , [ " interface " , ifname , " lower_up " ] , _old , true , _meta } ,
state
) do
2019-07-01 10:39:58 -06:00
FarmbotCore.Logger . success ( 1 , " Interface #{ ifname } connected access point " )
2019-10-24 11:37:17 -06:00
FarmbotTelemetry . event ( :network , :interface_connect , nil , interface : ifname )
2019-06-25 16:28:08 -06:00
state = cancel_network_not_found_timer ( state )
{ :noreply , state }
end
def handle_info (
2020-02-11 15:25:32 -07:00
{ VintageNet , [ " interface " , ifname , " connection " ] , :disconnected , :lan ,
_meta } ,
2019-06-25 16:28:08 -06:00
state
) do
2020-03-14 15:05:23 -06:00
FarmbotCore.Logger . success (
2020-01-17 08:58:53 -07:00
1 ,
" Interface #{ ifname } connected to local area network "
)
2019-10-24 11:37:17 -06:00
FarmbotTelemetry . event ( :network , :lan_connect , nil , interface : ifname )
2019-06-25 16:28:08 -06:00
{ :noreply , state }
end
2019-07-01 10:39:58 -06:00
def handle_info (
2020-02-11 15:25:32 -07:00
{ VintageNet , [ " interface " , ifname , " connection " ] , :lan , :internet ,
_meta } ,
2019-07-01 10:39:58 -06:00
state
) do
2020-03-14 15:05:23 -06:00
FarmbotCore.Logger . success ( 1 , " Interface #{ ifname } connected to internet " )
2019-06-25 16:28:08 -06:00
state = cancel_network_not_found_timer ( state )
2019-10-24 11:37:17 -06:00
FarmbotTelemetry . event ( :network , :wan_connect , nil , interface : ifname )
2019-06-25 16:28:08 -06:00
{ :noreply , %{ state | first_connect? : false } }
end
2019-07-01 10:39:58 -06:00
def handle_info (
2020-02-11 15:25:32 -07:00
{ VintageNet , [ " interface " , ifname , " connection " ] , :internet , ifstate ,
_meta } ,
2019-07-01 10:39:58 -06:00
state
) do
2020-01-17 08:58:53 -07:00
FarmbotCore.Logger . warn (
1 ,
" Interface #{ ifname } disconnected from the internet: #{ ifstate } "
)
2019-07-11 14:21:35 -06:00
FarmbotExt.AMQP.ConnectionWorker . close ( )
2019-10-24 11:37:17 -06:00
FarmbotTelemetry . event ( :network , :wan_disconnect , nil , interface : ifname )
2019-06-25 16:28:08 -06:00
if state . network_not_found_timer do
{ :noreply , state }
else
state = start_network_not_found_timer ( state )
{ :noreply , state }
end
end
def handle_info (
2020-02-11 15:25:32 -07:00
{ VintageNet , [ " interface " , _ , " wifi " , " access_points " ] , _old , _new ,
_meta } ,
2019-06-25 16:28:08 -06:00
state
) do
{ :noreply , state }
2019-10-29 13:14:33 -06:00
end
def handle_info (
2020-02-11 15:25:32 -07:00
{ VintageNet , [ " interface " , _ifname , " eap_status " ] , _old ,
%{ status : :success } = eap_status , _meta } ,
2019-10-29 13:14:33 -06:00
state
) do
FarmbotCore.Logger . debug ( 3 , """
Farmbot successfully completed EAP Authentication .
#{inspect(eap_status, limit: :infinity)}
""" )
{ :noreply , state }
end
def handle_info (
2020-02-11 15:25:32 -07:00
{ VintageNet , [ " interface " , _ifname , " eap_status " ] , _old ,
%{ status : :failure } , _meta } ,
2019-10-29 13:14:33 -06:00
state
) do
FarmbotCore.Logger . error ( 1 , """
Farmbot was unable to assosiate with the EAP network .
Please check the identity , password and method of connection
""" )
FarmbotOS.System . factory_reset ( """
2020-02-11 14:04:31 -07:00
Farmbot was unable to assosiate with the EAP network .
2019-10-29 13:14:33 -06:00
Please check the identity , password and method of connection
""" )
{ :noreply , state }
2019-06-25 16:28:08 -06:00
end
def handle_info ( { VintageNet , property , old , new , _meta } , state ) do
2019-07-09 10:39:06 -06:00
Logger . debug ( """
2019-06-25 16:28:08 -06:00
Unknown property change : #{inspect(property)}
2020-02-11 14:04:31 -07:00
old :
2019-06-25 16:28:08 -06:00
#{inspect(old, limit: :infinity)}
2020-02-11 14:04:31 -07:00
new :
2019-06-25 16:28:08 -06:00
#{inspect(new, limit: :infinity)}
""" )
{ :noreply , state }
end
def handle_info ( { :network_not_found_timer , minutes } , state ) do
FarmbotCore.Logger . warn ( 1 , """
2020-02-11 14:04:31 -07:00
Farmbot has been disconnected from the network for
2019-07-03 11:50:26 -06:00
#{minutes} minutes. Going down for factory reset.
""" )
2019-07-03 13:05:01 -06:00
2019-07-03 11:50:26 -06:00
FarmbotOS.System . factory_reset ( """
2020-02-11 14:04:31 -07:00
Farmbot has been disconnected from the network for
2019-07-03 11:50:26 -06:00
#{minutes} minutes.
2019-06-25 16:28:08 -06:00
""" )
2019-07-03 13:05:01 -06:00
2019-06-25 16:28:08 -06:00
{ :noreply , state }
end
def to_vintage_net ( % Config.NetworkInterface { } = config ) do
%{
type : Validator ,
network_type : config . type ,
ssid : config . ssid ,
security : config . security ,
psk : config . psk ,
identity : config . identity ,
password : config . password ,
domain : config . domain ,
name_servers : config . name_servers ,
ipv4_method : config . ipv4_method ,
ipv4_address : config . ipv4_address ,
ipv4_gateway : config . ipv4_gateway ,
ipv4_subnet_mask : config . ipv4_subnet_mask ,
regulatory_domain : config . regulatory_domain
}
end
defp cancel_network_not_found_timer ( state ) do
2019-07-01 10:39:58 -06:00
FarmbotCore.Logger . success (
1 ,
" Farmbot has been reconnected. Canceling scheduled factory reset "
)
2019-06-25 16:28:08 -06:00
old_timer = state . network_not_found_timer
old_timer && Process . cancel_timer ( old_timer )
%{ state | network_not_found_timer : nil }
end
defp start_network_not_found_timer ( state ) do
state = cancel_network_not_found_timer ( state )
# Stored in minutes
minutes = network_not_found_timer_minutes ( state )
millis = minutes * 60000
2020-01-17 08:58:53 -07:00
2020-02-11 15:25:32 -07:00
new_timer =
Process . send_after ( self ( ) , { :network_not_found_timer , minutes } , millis )
2019-06-25 16:28:08 -06:00
2019-10-02 12:50:22 -06:00
FarmbotCore.Logger . warn (
1 ,
" FarmBot will factory reset in #{ minutes } minutes if the network does not reassociate. If you see this message directly after configuration, this message can be safely ignored. "
)
2019-06-25 16:28:08 -06:00
%{ state | network_not_found_timer : new_timer }
end
# if the network has never connected before, make a low
# thresh so that user won't have to wait 20 minutes to reconfigurate
# due to bad wifi credentials.
defp network_not_found_timer_minutes ( %{ first_connect? : true } ) , do : 1
defp network_not_found_timer_minutes ( _state ) do
2020-01-17 08:58:53 -07:00
Asset . fbos_config ( :network_not_found_timer ) ||
@default_network_not_found_timer_minutes
2019-06-25 16:28:08 -06:00
end
2019-07-11 13:10:46 -06:00
2019-07-19 09:08:13 -06:00
def reset_ntp do
2019-10-24 11:37:17 -06:00
FarmbotTelemetry . event ( :ntp , :reset )
2020-01-17 08:58:53 -07:00
2020-02-11 15:25:32 -07:00
ntp_server_1 =
Config . get_config_value ( :string , " settings " , " default_ntp_server_1 " )
2020-01-17 08:58:53 -07:00
2020-02-11 15:25:32 -07:00
ntp_server_2 =
Config . get_config_value ( :string , " settings " , " default_ntp_server_2 " )
2019-07-16 09:59:32 -06:00
if ntp_server_1 || ntp_server_2 do
Logger . info ( " Setting NTP servers: [ #{ ntp_server_1 } , #{ ntp_server_2 } ] " )
[ ntp_server_1 , ntp_server_2 ]
|> Enum . reject ( & is_nil / 1 )
2019-12-16 15:01:59 -07:00
|> NervesTime . set_ntp_servers ( )
2019-07-16 09:59:32 -06:00
else
Logger . info ( " Using default NTP servers " )
:ok
end
2019-07-11 13:10:46 -06:00
end
2018-11-28 13:12:25 -07:00
end