Compare commits
24 Commits
feature/do
...
8764e1b45a
Author | SHA1 | Date | |
---|---|---|---|
8764e1b45a | |||
6e61fc7756 | |||
f0af8f08e3 | |||
76c3787107 | |||
544df78ac8 | |||
02df37692f | |||
ed4e207ff0 | |||
a5a37fdd1b | |||
1b52e16db5 | |||
c9550ea5b8 | |||
3de33014e5 | |||
a458ad1712 | |||
030b259d32 | |||
022852b163 | |||
2d5eca233e | |||
9d6e0cd453 | |||
a2c3112f81 | |||
c7e3fcabcf | |||
70c4c91035 | |||
aa39597541 | |||
bcbab1383a | |||
bab0984c30 | |||
0667c6ec39 | |||
0d71899ce1 |
@ -35,8 +35,6 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
|
||||||
<PackageReference Include="MQTTnet" Version="3.0.13" />
|
|
||||||
<PackageReference Include="MQTTnet.AspNetCore" Version="3.0.13" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="NLog" Version="4.7.5" />
|
<PackageReference Include="NLog" Version="4.7.5" />
|
||||||
<PackageReference Include="NLog.Web" Version="4.9.3" />
|
<PackageReference Include="NLog.Web" Version="4.9.3" />
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
import { Box, Container, IconButton, Menu, MenuItem, MenuList, Paper, Grow, Popper } from '@material-ui/core';
|
import { Box, Paper } from '@material-ui/core';
|
||||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
import { blueGrey, grey, orange } from '@material-ui/core/colors';
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
|
||||||
import { positions } from '@material-ui/system';
|
|
||||||
import { createMuiTheme, createStyles, makeStyles, Theme } from '@material-ui/core/styles';
|
import { createMuiTheme, createStyles, makeStyles, Theme } from '@material-ui/core/styles';
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
|
||||||
import Typography from '@material-ui/core/Typography';
|
|
||||||
import { ThemeProvider } from '@material-ui/styles';
|
import { ThemeProvider } from '@material-ui/styles';
|
||||||
import React, { useState, } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { BrowserRouter, NavLink, Redirect, Route, Switch, Link } from 'react-router-dom';
|
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';
|
||||||
import BirdmapTitle from './components/appBar/BirdmapTitle';
|
import BirdmapBar from './components/appBar/BirdmapBar';
|
||||||
import Auth from './components/auth/Auth';
|
import Auth from './components/auth/Auth';
|
||||||
import AuthService from './components/auth/AuthService';
|
import AuthService from './components/auth/AuthService';
|
||||||
import { ClickAwayListener } from '@material-ui/core';
|
|
||||||
import MapContainer from './components/heatmap/Heatmap';
|
|
||||||
import Devices from './components/devices/Devices';
|
|
||||||
import { blueGrey, blue, orange, grey } from '@material-ui/core/colors';
|
|
||||||
import DevicesContextProvider from './contexts/DevicesContextProvider'
|
|
||||||
import Dashboard from './components/dashboard/Dashboard';
|
import Dashboard from './components/dashboard/Dashboard';
|
||||||
|
import Devices from './components/devices/Devices';
|
||||||
|
import MapContainer from './components/heatmap/Heatmap';
|
||||||
|
import Logs from './components/logs/Logs';
|
||||||
|
import DevicesContextProvider from './contexts/DevicesContextProvider';
|
||||||
|
|
||||||
const theme = createMuiTheme({
|
const theme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
@ -26,14 +20,13 @@ const theme = createMuiTheme({
|
|||||||
dark: grey[400],
|
dark: grey[400],
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: orange[200],
|
main: blueGrey[700],
|
||||||
dark: blueGrey[50],
|
dark: blueGrey[50],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const [authenticated, setAuthenticated] = useState(AuthService.isAuthenticated());
|
const [authenticated, setAuthenticated] = useState(AuthService.isAuthenticated());
|
||||||
const [isAdmin, setIsAdmin] = useState(AuthService.isAdmin());
|
const [isAdmin, setIsAdmin] = useState(AuthService.isAdmin());
|
||||||
|
|
||||||
@ -48,6 +41,10 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LogsComponent = () => {
|
||||||
|
return <Logs/>
|
||||||
|
}
|
||||||
|
|
||||||
const DashboardComponent = () => {
|
const DashboardComponent = () => {
|
||||||
return <Dashboard isAdmin={isAdmin}/>;
|
return <Dashboard isAdmin={isAdmin}/>;
|
||||||
};
|
};
|
||||||
@ -56,6 +53,7 @@ function App() {
|
|||||||
return <Devices isAdmin={isAdmin}/>;
|
return <Devices isAdmin={isAdmin}/>;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const HeatmapComponent = () => {
|
const HeatmapComponent = () => {
|
||||||
return (
|
return (
|
||||||
<Paper elevation={0}>
|
<Paper elevation={0}>
|
||||||
@ -64,15 +62,46 @@ function App() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const HeaderComponent = () => {
|
||||||
|
return (
|
||||||
|
<BirdmapBar onLogout={AuthService.logout} isAdmin={isAdmin} isAuthenticated={authenticated}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PredicateRoute = ({ component: Component, predicate: Predicate, ...rest }: { [x: string]: any, component: any, predicate: any }) => {
|
||||||
|
return (
|
||||||
|
<PredicateRouteInternal {...rest} header={HeaderComponent} body={Component} predicate={Predicate}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PublicRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => {
|
||||||
|
return (
|
||||||
|
<PredicateRoute {...rest} component={Component} predicate={true}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PrivateRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => {
|
||||||
|
return (
|
||||||
|
<PredicateRoute {...rest} component={Component} predicate={authenticated}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => {
|
||||||
|
return (
|
||||||
|
<PredicateRoute {...rest} component={Component} predicate={authenticated && isAdmin}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Switch>
|
<Switch>
|
||||||
<PublicRoute path="/login" component={AuthComponent} />
|
<PublicRoute exact path="/login" component={AuthComponent} />
|
||||||
|
<AdminRoute exact path="/logs" component={LogsComponent} />
|
||||||
<DevicesContextProvider>
|
<DevicesContextProvider>
|
||||||
<PrivateRoute path="/" exact authenticated={authenticated} component={DashboardComponent} />
|
<PrivateRoute exact path="/" component={DashboardComponent} />
|
||||||
<PrivateRoute path="/devices/:id?" exact authenticated={authenticated} component={DevicesComponent} />
|
<PrivateRoute exact path="/devices/:id?" component={DevicesComponent} />
|
||||||
<PrivateRoute path="/heatmap" exact authenticated={authenticated} component={HeatmapComponent} />
|
<PrivateRoute exact path="/heatmap" component={HeatmapComponent} />
|
||||||
</DevicesContextProvider>
|
</DevicesContextProvider>
|
||||||
</Switch>
|
</Switch>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
@ -82,112 +111,26 @@ function App() {
|
|||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
||||||
const PublicRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any}) => {
|
const PredicateRouteInternal = ({ header: HeaderComponent, body: BodyComponent, predicate: Predicate, ...rest }: { [x: string]: any, header: any, body: any, predicate: any }) => {
|
||||||
return (
|
return (
|
||||||
<Route {...rest} render={matchProps => (
|
<Route {...rest} render={matchProps => (
|
||||||
<DefaultLayout component={Component} authenticated={false} isAdmin={false} {...matchProps} />
|
Predicate
|
||||||
)} />
|
? <DefaultLayoutInternal header={HeaderComponent} body={BodyComponent} {...matchProps} />
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PrivateRoute = ({ component: Component, authenticated: Authenticated, ...rest }: { [x: string]: any, component: any, authenticated: any }) => {
|
|
||||||
return (
|
|
||||||
<Route {...rest} render={matchProps => (
|
|
||||||
Authenticated
|
|
||||||
? <DefaultLayout component={Component} authenticated={Authenticated} {...matchProps} />
|
|
||||||
: <Redirect to='/login' />
|
: <Redirect to='/login' />
|
||||||
)} />
|
)} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DefaultLayout = ({ component: Component, authenticated: Authenticated, ...rest }: { [x: string]: any, component: any, authenticated: any }) => {
|
const DefaultLayoutInternal = ({ header: HeaderComponent, body: BodyComponent, ...rest }: { [x: string]: any, header: any, body: any }) => {
|
||||||
const classes = useDefaultLayoutStyles();
|
const classes = useDefaultLayoutStyles();
|
||||||
const [open, setOpen] = React.useState(false);
|
|
||||||
const anchorRef = React.useRef<HTMLButtonElement>(null);
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen((prevOpen) => !prevOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (event: React.MouseEvent<EventTarget>) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLogout = (event: React.MouseEvent<EventTarget>) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthService.logout();
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleListKeyDown(event: React.KeyboardEvent) {
|
|
||||||
if (event.key === 'Tab') {
|
|
||||||
event.preventDefault();
|
|
||||||
setOpen(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevOpen = React.useRef(open);
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (prevOpen.current === true && open === false) {
|
|
||||||
anchorRef.current!.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
prevOpen.current = open;
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
|
|
||||||
const renderNavLinks = () => {
|
|
||||||
return Authenticated
|
|
||||||
? <Container className={classes.nav_menu}>
|
|
||||||
<NavLink exact to="/" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Dashboard</NavLink>
|
|
||||||
<NavLink to="/devices" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Devices</NavLink>
|
|
||||||
<NavLink exact to="/heatmap" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Heatmap</NavLink>
|
|
||||||
<IconButton className={classes.nav_menu_icon}
|
|
||||||
ref={anchorRef}
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-controls={open ? 'menu-list-grow' : undefined}
|
|
||||||
aria-label="account of current user"
|
|
||||||
onClick={handleToggle}>
|
|
||||||
<AccountCircle/>
|
|
||||||
</IconButton>
|
|
||||||
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
|
|
||||||
{({ TransitionProps, placement }) => (
|
|
||||||
<Grow
|
|
||||||
{...TransitionProps}
|
|
||||||
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}>
|
|
||||||
<Paper>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
|
|
||||||
<MenuItem onClick={handleLogout} component={Link} {...{ to: '/login' }}>Logout</MenuItem>
|
|
||||||
</MenuList>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Grow>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
</Container>
|
|
||||||
: null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<AppBar position="static" className={classes.bar_root}>
|
<Box className={classes.header}>
|
||||||
<Toolbar>
|
<HeaderComponent />
|
||||||
<BirdmapTitle />
|
</Box>
|
||||||
<Typography component={'span'} className={classes.typo}>
|
<Box className={classes.body}>
|
||||||
{renderNavLinks()}
|
<BodyComponent {...rest} />
|
||||||
</Typography>
|
|
||||||
</Toolbar>
|
|
||||||
</AppBar>
|
|
||||||
<Box zIndex="modal" className={classes.box_root}>
|
|
||||||
<Component {...rest} />
|
|
||||||
</Box>
|
</Box>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@ -195,46 +138,12 @@ const DefaultLayout = ({ component: Component, authenticated: Authenticated, ...
|
|||||||
|
|
||||||
const useDefaultLayoutStyles = makeStyles((theme: Theme) =>
|
const useDefaultLayoutStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
bar_root: {
|
header: {
|
||||||
height: '7%',
|
height: '7%',
|
||||||
},
|
},
|
||||||
box_root: {
|
body: {
|
||||||
backgroundColor: theme.palette.primary.dark,
|
backgroundColor: theme.palette.primary.dark,
|
||||||
height: '93%',
|
height: '93%',
|
||||||
},
|
}
|
||||||
typo: {
|
|
||||||
marginLeft: 'auto',
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
nav_menu: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
nav_menu_icon: {
|
|
||||||
color: 'inherit',
|
|
||||||
marginLeft: '24px',
|
|
||||||
'&:hover': {
|
|
||||||
color: 'inherit',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nav_menu_item: {
|
|
||||||
textDecoration: 'none',
|
|
||||||
fontWeight: 'normal',
|
|
||||||
color: 'inherit',
|
|
||||||
marginLeft: '24px',
|
|
||||||
'&:hover': {
|
|
||||||
color: 'inherit',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nav_menu_item_active: {
|
|
||||||
textDecoration: 'underline',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: 'inherit',
|
|
||||||
marginLeft: '24px',
|
|
||||||
'&:hover': {
|
|
||||||
color: 'inherit',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
136
Birdmap.API/ClientApp/src/components/appBar/BirdmapBar.tsx
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import { ClickAwayListener, Container, createStyles, Grow, IconButton, makeStyles, MenuItem, MenuList, Paper, Popper, Theme } from '@material-ui/core';
|
||||||
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||||
|
import React from 'react';
|
||||||
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
|
import BirdmapTitle from './BirdmapTitle';
|
||||||
|
|
||||||
|
export default function BirdmapBar(props: { onLogout: () => void; isAuthenticated: any; isAdmin: any; }) {
|
||||||
|
const classes = useAppbarStyles();
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const anchorRef = React.useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
setOpen((prevOpen) => !prevOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = (event: React.MouseEvent<EventTarget>) => {
|
||||||
|
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = (event: React.MouseEvent<EventTarget>) => {
|
||||||
|
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.onLogout();
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleListKeyDown(event: React.KeyboardEvent) {
|
||||||
|
if (event.key === 'Tab') {
|
||||||
|
event.preventDefault();
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevOpen = React.useRef(open);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (prevOpen.current === true && open === false) {
|
||||||
|
anchorRef.current!.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevOpen.current = open;
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
const renderNavLinks = () => {
|
||||||
|
return props.isAuthenticated
|
||||||
|
? <Container className={classes.nav_menu}>
|
||||||
|
<NavLink exact to="/" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Dashboard</NavLink>
|
||||||
|
{props.isAdmin ? <NavLink exact to="/logs" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Logs</NavLink> : null}
|
||||||
|
<NavLink to="/devices" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Devices</NavLink>
|
||||||
|
<NavLink exact to="/heatmap" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Heatmap</NavLink>
|
||||||
|
<IconButton className={classes.nav_menu_icon}
|
||||||
|
ref={anchorRef}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-controls={open ? 'menu-list-grow' : undefined}
|
||||||
|
aria-label="account of current user"
|
||||||
|
onClick={handleToggle}>
|
||||||
|
<AccountCircle />
|
||||||
|
</IconButton>
|
||||||
|
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
|
||||||
|
{({ TransitionProps, placement }) => (
|
||||||
|
<Grow
|
||||||
|
{...TransitionProps}
|
||||||
|
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}>
|
||||||
|
<Paper>
|
||||||
|
<ClickAwayListener onClickAway={handleClose}>
|
||||||
|
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
|
||||||
|
<MenuItem onClick={handleLogout} component={Link} {...{ to: '/login' }}>Logout</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</ClickAwayListener>
|
||||||
|
</Paper>
|
||||||
|
</Grow>
|
||||||
|
)}
|
||||||
|
</Popper>
|
||||||
|
</Container>
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppBar position="static">
|
||||||
|
<Toolbar>
|
||||||
|
<BirdmapTitle />
|
||||||
|
<Typography component={'span'} className={classes.typo}>
|
||||||
|
{renderNavLinks()}
|
||||||
|
</Typography>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const useAppbarStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
typo: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
nav_menu: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
nav_menu_icon: {
|
||||||
|
color: 'inherit',
|
||||||
|
marginLeft: '24px',
|
||||||
|
'&:hover': {
|
||||||
|
color: 'inherit',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nav_menu_item: {
|
||||||
|
textDecoration: 'none',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
color: 'inherit',
|
||||||
|
marginLeft: '24px',
|
||||||
|
'&:hover': {
|
||||||
|
color: 'inherit',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nav_menu_item_active: {
|
||||||
|
textDecoration: 'underline',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'inherit',
|
||||||
|
marginLeft: '24px',
|
||||||
|
'&:hover': {
|
||||||
|
color: 'inherit',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
@ -1,4 +1,5 @@
|
|||||||
/*global google*/
|
/*global google*/
|
||||||
|
import { Box, withStyles } from '@material-ui/core';
|
||||||
import GoogleMapReact from 'google-map-react';
|
import GoogleMapReact from 'google-map-react';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import C from '../../common/Constants';
|
import C from '../../common/Constants';
|
||||||
@ -8,7 +9,14 @@ import DeviceMarker from './DeviceMarker';
|
|||||||
const lat_offset = 0.000038;
|
const lat_offset = 0.000038;
|
||||||
const lng_offset = -0.000058;
|
const lng_offset = -0.000058;
|
||||||
|
|
||||||
export default class MapContainer extends Component {
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
height: '93vh',
|
||||||
|
width: '100%',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class MapContainer extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -64,6 +72,8 @@ export default class MapContainer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {classes} = this.props;
|
||||||
|
|
||||||
const heatMapData = {
|
const heatMapData = {
|
||||||
positions: this.state.heatmapPoints,
|
positions: this.state.heatmapPoints,
|
||||||
options: {
|
options: {
|
||||||
@ -92,7 +102,7 @@ export default class MapContainer extends Component {
|
|||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: '93vh', width: '100%' }}>
|
<Box className={classes.root}>
|
||||||
<GoogleMapReact
|
<GoogleMapReact
|
||||||
bootstrapURLKeys={{
|
bootstrapURLKeys={{
|
||||||
key: ["AIzaSyCZ51VFfxqZ2GkCmVrcNZdUKsM0fuBQUCY"],
|
key: ["AIzaSyCZ51VFfxqZ2GkCmVrcNZdUKsM0fuBQUCY"],
|
||||||
@ -106,7 +116,9 @@ export default class MapContainer extends Component {
|
|||||||
defaultCenter={this.state.center}>
|
defaultCenter={this.state.center}>
|
||||||
{Markers}
|
{Markers}
|
||||||
</GoogleMapReact>
|
</GoogleMapReact>
|
||||||
</div>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(MapContainer);
|
208
Birdmap.API/ClientApp/src/components/logs/LogService.js
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
"use strict";
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
//----------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||||
|
// </auto-generated>
|
||||||
|
//----------------------
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
var __extends = (this && this.__extends) || (function () {
|
||||||
|
var extendStatics = function (d, b) {
|
||||||
|
extendStatics = Object.setPrototypeOf ||
|
||||||
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||||
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||||
|
return extendStatics(d, b);
|
||||||
|
};
|
||||||
|
return function (d, b) {
|
||||||
|
extendStatics(d, b);
|
||||||
|
function __() { this.constructor = d; }
|
||||||
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.ApiException = exports.HttpStatusCode = void 0;
|
||||||
|
var LogService = /** @class */ (function () {
|
||||||
|
function LogService(baseUrl, http) {
|
||||||
|
this.jsonParseReviver = undefined;
|
||||||
|
this.http = http ? http : window;
|
||||||
|
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "api/logs";
|
||||||
|
}
|
||||||
|
LogService.prototype.getAll = function () {
|
||||||
|
var _this = this;
|
||||||
|
var url_ = this.baseUrl + "/all";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
var options_ = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
'Authorization': sessionStorage.getItem('user')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return this.http.fetch(url_, options_).then(function (_response) {
|
||||||
|
return _this.processGetAll(_response);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LogService.prototype.processGetAll = function (response) {
|
||||||
|
var _this = this;
|
||||||
|
var status = response.status;
|
||||||
|
var _headers = {};
|
||||||
|
if (response.headers && response.headers.forEach) {
|
||||||
|
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||||
|
}
|
||||||
|
;
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then(function (_responseText) {
|
||||||
|
var result200 = null;
|
||||||
|
var resultData200 = _responseText === "" ? null : JSON.parse(_responseText, _this.jsonParseReviver);
|
||||||
|
if (Array.isArray(resultData200)) {
|
||||||
|
result200 = [];
|
||||||
|
for (var _i = 0, resultData200_1 = resultData200; _i < resultData200_1.length; _i++) {
|
||||||
|
var item = resultData200_1[_i];
|
||||||
|
result200.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then(function (_responseText) {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve(null);
|
||||||
|
};
|
||||||
|
LogService.prototype.getFiles = function (filenames) {
|
||||||
|
var _this = this;
|
||||||
|
var url_ = this.baseUrl + "?";
|
||||||
|
if (filenames !== undefined && filenames !== null)
|
||||||
|
filenames && filenames.forEach(function (item) { url_ += "filenames=" + encodeURIComponent("" + item) + "&"; });
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
var options_ = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/octet-stream",
|
||||||
|
'Authorization': sessionStorage.getItem('user')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return this.http.fetch(url_, options_).then(function (_response) {
|
||||||
|
return _this.processGetFiles(_response);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LogService.prototype.processGetFiles = function (response) {
|
||||||
|
var status = response.status;
|
||||||
|
var _headers = {};
|
||||||
|
if (response.headers && response.headers.forEach) {
|
||||||
|
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||||
|
}
|
||||||
|
;
|
||||||
|
if (status === 200 || status === 206) {
|
||||||
|
var contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||||
|
var fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||||
|
var fileName_1 = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||||
|
return response.blob().then(function (blob) { return { fileName: fileName_1, data: blob, status: status, headers: _headers }; });
|
||||||
|
}
|
||||||
|
else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then(function (_responseText) {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve(null);
|
||||||
|
};
|
||||||
|
return LogService;
|
||||||
|
}());
|
||||||
|
exports.default = LogService;
|
||||||
|
var HttpStatusCode;
|
||||||
|
(function (HttpStatusCode) {
|
||||||
|
HttpStatusCode["Continue"] = "Continue";
|
||||||
|
HttpStatusCode["SwitchingProtocols"] = "SwitchingProtocols";
|
||||||
|
HttpStatusCode["Processing"] = "Processing";
|
||||||
|
HttpStatusCode["EarlyHints"] = "EarlyHints";
|
||||||
|
HttpStatusCode["OK"] = "OK";
|
||||||
|
HttpStatusCode["Created"] = "Created";
|
||||||
|
HttpStatusCode["Accepted"] = "Accepted";
|
||||||
|
HttpStatusCode["NonAuthoritativeInformation"] = "NonAuthoritativeInformation";
|
||||||
|
HttpStatusCode["NoContent"] = "NoContent";
|
||||||
|
HttpStatusCode["ResetContent"] = "ResetContent";
|
||||||
|
HttpStatusCode["PartialContent"] = "PartialContent";
|
||||||
|
HttpStatusCode["MultiStatus"] = "MultiStatus";
|
||||||
|
HttpStatusCode["AlreadyReported"] = "AlreadyReported";
|
||||||
|
HttpStatusCode["IMUsed"] = "IMUsed";
|
||||||
|
HttpStatusCode["MultipleChoices"] = "Ambiguous";
|
||||||
|
HttpStatusCode["Ambiguous"] = "Ambiguous";
|
||||||
|
HttpStatusCode["MovedPermanently"] = "Moved";
|
||||||
|
HttpStatusCode["Moved"] = "Moved";
|
||||||
|
HttpStatusCode["Found"] = "Redirect";
|
||||||
|
HttpStatusCode["Redirect"] = "Redirect";
|
||||||
|
HttpStatusCode["SeeOther"] = "RedirectMethod";
|
||||||
|
HttpStatusCode["RedirectMethod"] = "RedirectMethod";
|
||||||
|
HttpStatusCode["NotModified"] = "NotModified";
|
||||||
|
HttpStatusCode["UseProxy"] = "UseProxy";
|
||||||
|
HttpStatusCode["Unused"] = "Unused";
|
||||||
|
HttpStatusCode["TemporaryRedirect"] = "TemporaryRedirect";
|
||||||
|
HttpStatusCode["RedirectKeepVerb"] = "TemporaryRedirect";
|
||||||
|
HttpStatusCode["PermanentRedirect"] = "PermanentRedirect";
|
||||||
|
HttpStatusCode["BadRequest"] = "BadRequest";
|
||||||
|
HttpStatusCode["Unauthorized"] = "Unauthorized";
|
||||||
|
HttpStatusCode["PaymentRequired"] = "PaymentRequired";
|
||||||
|
HttpStatusCode["Forbidden"] = "Forbidden";
|
||||||
|
HttpStatusCode["NotFound"] = "NotFound";
|
||||||
|
HttpStatusCode["MethodNotAllowed"] = "MethodNotAllowed";
|
||||||
|
HttpStatusCode["NotAcceptable"] = "NotAcceptable";
|
||||||
|
HttpStatusCode["ProxyAuthenticationRequired"] = "ProxyAuthenticationRequired";
|
||||||
|
HttpStatusCode["RequestTimeout"] = "RequestTimeout";
|
||||||
|
HttpStatusCode["Conflict"] = "Conflict";
|
||||||
|
HttpStatusCode["Gone"] = "Gone";
|
||||||
|
HttpStatusCode["LengthRequired"] = "LengthRequired";
|
||||||
|
HttpStatusCode["PreconditionFailed"] = "PreconditionFailed";
|
||||||
|
HttpStatusCode["RequestEntityTooLarge"] = "RequestEntityTooLarge";
|
||||||
|
HttpStatusCode["RequestUriTooLong"] = "RequestUriTooLong";
|
||||||
|
HttpStatusCode["UnsupportedMediaType"] = "UnsupportedMediaType";
|
||||||
|
HttpStatusCode["RequestedRangeNotSatisfiable"] = "RequestedRangeNotSatisfiable";
|
||||||
|
HttpStatusCode["ExpectationFailed"] = "ExpectationFailed";
|
||||||
|
HttpStatusCode["MisdirectedRequest"] = "MisdirectedRequest";
|
||||||
|
HttpStatusCode["UnprocessableEntity"] = "UnprocessableEntity";
|
||||||
|
HttpStatusCode["Locked"] = "Locked";
|
||||||
|
HttpStatusCode["FailedDependency"] = "FailedDependency";
|
||||||
|
HttpStatusCode["UpgradeRequired"] = "UpgradeRequired";
|
||||||
|
HttpStatusCode["PreconditionRequired"] = "PreconditionRequired";
|
||||||
|
HttpStatusCode["TooManyRequests"] = "TooManyRequests";
|
||||||
|
HttpStatusCode["RequestHeaderFieldsTooLarge"] = "RequestHeaderFieldsTooLarge";
|
||||||
|
HttpStatusCode["UnavailableForLegalReasons"] = "UnavailableForLegalReasons";
|
||||||
|
HttpStatusCode["InternalServerError"] = "InternalServerError";
|
||||||
|
HttpStatusCode["NotImplemented"] = "NotImplemented";
|
||||||
|
HttpStatusCode["BadGateway"] = "BadGateway";
|
||||||
|
HttpStatusCode["ServiceUnavailable"] = "ServiceUnavailable";
|
||||||
|
HttpStatusCode["GatewayTimeout"] = "GatewayTimeout";
|
||||||
|
HttpStatusCode["HttpVersionNotSupported"] = "HttpVersionNotSupported";
|
||||||
|
HttpStatusCode["VariantAlsoNegotiates"] = "VariantAlsoNegotiates";
|
||||||
|
HttpStatusCode["InsufficientStorage"] = "InsufficientStorage";
|
||||||
|
HttpStatusCode["LoopDetected"] = "LoopDetected";
|
||||||
|
HttpStatusCode["NotExtended"] = "NotExtended";
|
||||||
|
HttpStatusCode["NetworkAuthenticationRequired"] = "NetworkAuthenticationRequired";
|
||||||
|
})(HttpStatusCode = exports.HttpStatusCode || (exports.HttpStatusCode = {}));
|
||||||
|
var ApiException = /** @class */ (function (_super) {
|
||||||
|
__extends(ApiException, _super);
|
||||||
|
function ApiException(message, status, response, headers, result) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.isApiException = true;
|
||||||
|
_this.message = message;
|
||||||
|
_this.status = status;
|
||||||
|
_this.response = response;
|
||||||
|
_this.headers = headers;
|
||||||
|
_this.result = result;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
ApiException.isApiException = function (obj) {
|
||||||
|
return obj.isApiException === true;
|
||||||
|
};
|
||||||
|
return ApiException;
|
||||||
|
}(Error));
|
||||||
|
exports.ApiException = ApiException;
|
||||||
|
function throwException(message, status, response, headers, result) {
|
||||||
|
if (result !== null && result !== undefined)
|
||||||
|
throw result;
|
||||||
|
else
|
||||||
|
throw new ApiException(message, status, response, headers, null);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=LogService.js.map
|
201
Birdmap.API/ClientApp/src/components/logs/LogService.ts
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
//----------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||||
|
// </auto-generated>
|
||||||
|
//----------------------
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
export default class LogService {
|
||||||
|
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||||
|
private baseUrl: string;
|
||||||
|
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||||
|
|
||||||
|
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||||
|
this.http = http ? http : <any>window;
|
||||||
|
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "api/logs";
|
||||||
|
}
|
||||||
|
|
||||||
|
getAll(): Promise<string[]> {
|
||||||
|
let url_ = this.baseUrl + "/all";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_ = <RequestInit>{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
'Authorization': sessionStorage.getItem('user')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||||
|
return this.processGetAll(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processGetAll(response: Response): Promise<string[]> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result200: any = null;
|
||||||
|
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||||
|
if (Array.isArray(resultData200)) {
|
||||||
|
result200 = [] as any;
|
||||||
|
for (let item of resultData200)
|
||||||
|
result200!.push(item);
|
||||||
|
}
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<string[]>(<any>null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFiles(filenames: string[] | null | undefined): Promise<FileResponse | null> {
|
||||||
|
let url_ = this.baseUrl + "?";
|
||||||
|
if (filenames !== undefined && filenames !== null)
|
||||||
|
filenames && filenames.forEach(item => { url_ += "filenames=" + encodeURIComponent("" + item) + "&"; });
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_ = <RequestInit>{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/octet-stream",
|
||||||
|
'Authorization': sessionStorage.getItem('user')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||||
|
return this.processGetFiles(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processGetFiles(response: Response): Promise<FileResponse | null> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200 || status === 206) {
|
||||||
|
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||||
|
const fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||||
|
const fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||||
|
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<FileResponse | null>(<any>null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileResponse {
|
||||||
|
data: Blob;
|
||||||
|
status: number;
|
||||||
|
fileName?: string;
|
||||||
|
headers?: { [name: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HttpStatusCode {
|
||||||
|
Continue = "Continue",
|
||||||
|
SwitchingProtocols = "SwitchingProtocols",
|
||||||
|
Processing = "Processing",
|
||||||
|
EarlyHints = "EarlyHints",
|
||||||
|
OK = "OK",
|
||||||
|
Created = "Created",
|
||||||
|
Accepted = "Accepted",
|
||||||
|
NonAuthoritativeInformation = "NonAuthoritativeInformation",
|
||||||
|
NoContent = "NoContent",
|
||||||
|
ResetContent = "ResetContent",
|
||||||
|
PartialContent = "PartialContent",
|
||||||
|
MultiStatus = "MultiStatus",
|
||||||
|
AlreadyReported = "AlreadyReported",
|
||||||
|
IMUsed = "IMUsed",
|
||||||
|
MultipleChoices = "Ambiguous",
|
||||||
|
Ambiguous = "Ambiguous",
|
||||||
|
MovedPermanently = "Moved",
|
||||||
|
Moved = "Moved",
|
||||||
|
Found = "Redirect",
|
||||||
|
Redirect = "Redirect",
|
||||||
|
SeeOther = "RedirectMethod",
|
||||||
|
RedirectMethod = "RedirectMethod",
|
||||||
|
NotModified = "NotModified",
|
||||||
|
UseProxy = "UseProxy",
|
||||||
|
Unused = "Unused",
|
||||||
|
TemporaryRedirect = "TemporaryRedirect",
|
||||||
|
RedirectKeepVerb = "TemporaryRedirect",
|
||||||
|
PermanentRedirect = "PermanentRedirect",
|
||||||
|
BadRequest = "BadRequest",
|
||||||
|
Unauthorized = "Unauthorized",
|
||||||
|
PaymentRequired = "PaymentRequired",
|
||||||
|
Forbidden = "Forbidden",
|
||||||
|
NotFound = "NotFound",
|
||||||
|
MethodNotAllowed = "MethodNotAllowed",
|
||||||
|
NotAcceptable = "NotAcceptable",
|
||||||
|
ProxyAuthenticationRequired = "ProxyAuthenticationRequired",
|
||||||
|
RequestTimeout = "RequestTimeout",
|
||||||
|
Conflict = "Conflict",
|
||||||
|
Gone = "Gone",
|
||||||
|
LengthRequired = "LengthRequired",
|
||||||
|
PreconditionFailed = "PreconditionFailed",
|
||||||
|
RequestEntityTooLarge = "RequestEntityTooLarge",
|
||||||
|
RequestUriTooLong = "RequestUriTooLong",
|
||||||
|
UnsupportedMediaType = "UnsupportedMediaType",
|
||||||
|
RequestedRangeNotSatisfiable = "RequestedRangeNotSatisfiable",
|
||||||
|
ExpectationFailed = "ExpectationFailed",
|
||||||
|
MisdirectedRequest = "MisdirectedRequest",
|
||||||
|
UnprocessableEntity = "UnprocessableEntity",
|
||||||
|
Locked = "Locked",
|
||||||
|
FailedDependency = "FailedDependency",
|
||||||
|
UpgradeRequired = "UpgradeRequired",
|
||||||
|
PreconditionRequired = "PreconditionRequired",
|
||||||
|
TooManyRequests = "TooManyRequests",
|
||||||
|
RequestHeaderFieldsTooLarge = "RequestHeaderFieldsTooLarge",
|
||||||
|
UnavailableForLegalReasons = "UnavailableForLegalReasons",
|
||||||
|
InternalServerError = "InternalServerError",
|
||||||
|
NotImplemented = "NotImplemented",
|
||||||
|
BadGateway = "BadGateway",
|
||||||
|
ServiceUnavailable = "ServiceUnavailable",
|
||||||
|
GatewayTimeout = "GatewayTimeout",
|
||||||
|
HttpVersionNotSupported = "HttpVersionNotSupported",
|
||||||
|
VariantAlsoNegotiates = "VariantAlsoNegotiates",
|
||||||
|
InsufficientStorage = "InsufficientStorage",
|
||||||
|
LoopDetected = "LoopDetected",
|
||||||
|
NotExtended = "NotExtended",
|
||||||
|
NetworkAuthenticationRequired = "NetworkAuthenticationRequired",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApiException extends Error {
|
||||||
|
message: string;
|
||||||
|
status: number;
|
||||||
|
response: string;
|
||||||
|
headers: { [key: string]: any; };
|
||||||
|
result: any;
|
||||||
|
|
||||||
|
constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.message = message;
|
||||||
|
this.status = status;
|
||||||
|
this.response = response;
|
||||||
|
this.headers = headers;
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected isApiException = true;
|
||||||
|
|
||||||
|
static isApiException(obj: any): obj is ApiException {
|
||||||
|
return obj.isApiException === true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {
|
||||||
|
if (result !== null && result !== undefined)
|
||||||
|
throw result;
|
||||||
|
else
|
||||||
|
throw new ApiException(message, status, response, headers, null);
|
||||||
|
}
|
128
Birdmap.API/ClientApp/src/components/logs/Logs.jsx
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { Box, Button, Checkbox, List, ListItem, ListItemIcon, ListItemText, Paper, withStyles } from '@material-ui/core';
|
||||||
|
import { blueGrey } from '@material-ui/core/colors';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import LogService from './LogService';
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
padding: '64px',
|
||||||
|
backgroundColor: theme.palette.primary.dark,
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
backgroundColor: blueGrey[50],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class Logs extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
service: null,
|
||||||
|
files: [],
|
||||||
|
checked: [],
|
||||||
|
selectAllChecked: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
var service = new LogService();
|
||||||
|
this.setState({service: service});
|
||||||
|
|
||||||
|
service.getAll().then(result => {
|
||||||
|
this.setState({files: result});
|
||||||
|
}).catch(ex => console.log(ex));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToggle = (value) => {
|
||||||
|
const currentIndex = this.state.checked.indexOf(value);
|
||||||
|
const newChecked = [...this.state.checked];
|
||||||
|
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
newChecked.push(value);
|
||||||
|
} else {
|
||||||
|
newChecked.splice(currentIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({checked: newChecked});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelectAllToggle = () => {
|
||||||
|
this.setState({selectAllChecked: !this.state.selectAllChecked});
|
||||||
|
if (this.state.selectAllChecked) {
|
||||||
|
this.setState({checked: []});
|
||||||
|
} else {
|
||||||
|
const newChecked = [...this.state.files];
|
||||||
|
this.setState({checked: newChecked});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownload = () => {
|
||||||
|
this.state.service.getFiles(this.state.checked)
|
||||||
|
.then(result => {
|
||||||
|
const filename = `Logs-${new Date().toISOString()}.zip`;
|
||||||
|
const textUrl = URL.createObjectURL(result.data);
|
||||||
|
const element = document.createElement('a');
|
||||||
|
element.setAttribute('href', textUrl);
|
||||||
|
element.setAttribute('download', filename);
|
||||||
|
element.style.display = 'none';
|
||||||
|
document.body.appendChild(element);
|
||||||
|
element.click();
|
||||||
|
document.body.removeChild(element);
|
||||||
|
this.setState({checked: []});
|
||||||
|
this.setState({selectAllChecked: false});
|
||||||
|
})
|
||||||
|
.catch(ex => console.log(ex));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes } = this.props;
|
||||||
|
|
||||||
|
const Files = this.state.files.map((value) => {
|
||||||
|
const labelId = `checkbox-list-label-${value}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem key={value} role={undefined} dense button onClick={() => this.handleToggle(value)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Checkbox
|
||||||
|
edge="start"
|
||||||
|
checked={this.state.checked.indexOf(value) !== -1}
|
||||||
|
tabIndex={-1}
|
||||||
|
disableRipple
|
||||||
|
inputProps={{ 'aria-labelledby': labelId }}
|
||||||
|
/>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText id={labelId} primary={`${value}`} />
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className={classes.root}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<List className={classes.paper}>
|
||||||
|
<ListItem key="Select-all" role={undefined} dense button onClick={this.handleSelectAllToggle}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Checkbox
|
||||||
|
edge="start"
|
||||||
|
checked={this.state.selectAllChecked}
|
||||||
|
tabIndex={-1}
|
||||||
|
disableRipple
|
||||||
|
inputProps={{ 'aria-labelledby': "Select-all" }}
|
||||||
|
/>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText id="checkbox-list-label-Select-all" primary={(this.state.selectAllChecked ? "Uns" : "S") + "elect all"} />
|
||||||
|
</ListItem>
|
||||||
|
{Files}
|
||||||
|
</List>
|
||||||
|
<Button onClick={this.onDownload}>
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(Logs);
|
@ -1,15 +1,14 @@
|
|||||||
using Birdmap.BLL.Interfaces;
|
using Birdmap.BLL.Interfaces;
|
||||||
|
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Birdmap.API.Services.Hubs;
|
|
||||||
using Birdmap.API.Services;
|
|
||||||
|
|
||||||
namespace Birdmap.API.Controllers
|
namespace Birdmap.API.Controllers
|
||||||
{
|
{
|
||||||
|
66
Birdmap.API/Controllers/LogsController.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Birdmap.API.Controllers
|
||||||
|
{
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class LogsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<LogsController> _logger;
|
||||||
|
private readonly string _logFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Logs");
|
||||||
|
|
||||||
|
public LogsController(ILogger<LogsController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("all")]
|
||||||
|
public ActionResult<List<string>> GetAll()
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"Getting all log filenames from folder: '{_logFolderPath}'...");
|
||||||
|
|
||||||
|
return Directory.EnumerateFiles(_logFolderPath, "*.log")
|
||||||
|
.Select(f => Path.GetFileName(f))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetFiles([FromQuery] params string[] filenames)
|
||||||
|
{
|
||||||
|
if (!filenames.Any())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return await Task.Run(() =>
|
||||||
|
{
|
||||||
|
var zipStream = new MemoryStream();
|
||||||
|
|
||||||
|
using (var zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
|
||||||
|
{
|
||||||
|
foreach (var file in Directory.GetFiles(_logFolderPath, "*.log"))
|
||||||
|
{
|
||||||
|
var filename = Path.GetFileName(file);
|
||||||
|
|
||||||
|
if (filenames.Contains(filename))
|
||||||
|
{
|
||||||
|
zip.CreateEntryFromFile(file, filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zipStream.Position = 0;
|
||||||
|
|
||||||
|
return File(zipStream, "application/octet-stream");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Birdmap.API.DTOs;
|
using Birdmap.API.DTOs;
|
||||||
using Birdmap.API.Services;
|
|
||||||
using Birdmap.API.Services.Hubs;
|
|
||||||
using Birdmap.API.Services.Mqtt;
|
|
||||||
using Birdmap.BLL.Interfaces;
|
using Birdmap.BLL.Interfaces;
|
||||||
|
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||||
|
using Birdmap.BLL.Services.CommunicationServices.Mqtt;
|
||||||
using Birdmap.DAL.Entities;
|
using Birdmap.DAL.Entities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -55,24 +54,30 @@ namespace Birdmap.API.Controllers
|
|||||||
var serviceInfos = (await _service.GetAllServicesAsync())
|
var serviceInfos = (await _service.GetAllServicesAsync())
|
||||||
.Select(s => new ServiceInfo { Service = _mapper.Map<ServiceRequest>(s) }).ToList();
|
.Select(s => new ServiceInfo { Service = _mapper.Map<ServiceRequest>(s) }).ToList();
|
||||||
|
|
||||||
var client = new HttpClient();
|
var tasks = new List<Task>();
|
||||||
foreach (var si in serviceInfos)
|
foreach (var si in serviceInfos)
|
||||||
{
|
{
|
||||||
try
|
tasks.Add(Task.Run(async () =>
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"Sending a request to service [{si.Service.Name}] with url [{si.Service.Uri}]...");
|
var client = new HttpClient();
|
||||||
var response = await client.GetAsync(si.Service.Uri);
|
try
|
||||||
si.StatusCode = response.StatusCode;
|
{
|
||||||
si.Response = await response.Content.ReadAsStringAsync();
|
_logger.LogInformation($"Sending a request to service [{si.Service.Name}] with url [{si.Service.Uri}]...");
|
||||||
}
|
var response = await client.GetAsync(si.Service.Uri);
|
||||||
catch (Exception ex)
|
si.StatusCode = response.StatusCode;
|
||||||
{
|
si.Response = await response.Content.ReadAsStringAsync();
|
||||||
_logger.LogWarning($"Requesting service [{si.Service.Name}] faulted.");
|
}
|
||||||
si.StatusCode = HttpStatusCode.ServiceUnavailable;
|
catch (Exception ex)
|
||||||
si.Response = ex.ToString();
|
{
|
||||||
}
|
_logger.LogWarning($"Requesting service [{si.Service.Name}] faulted.");
|
||||||
|
si.StatusCode = HttpStatusCode.ServiceUnavailable;
|
||||||
|
si.Response = ex.ToString();
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
serviceInfos.Add(new()
|
serviceInfos.Add(new()
|
||||||
{
|
{
|
||||||
Service = new()
|
Service = new()
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
|
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apt-get update && apt-get install -y curl
|
RUN apt-get update && apt-get install -y curl
|
||||||
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
|
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
|
||||||
RUN apt-get update && apt-get install -y nodejs
|
RUN apt-get update && apt-get install -y nodejs
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
|
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||||
RUN apt-get update && apt-get install -y curl
|
RUN apt-get update && apt-get install -y curl
|
||||||
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
|
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
|
||||||
RUN apt-get update && apt-get install -y nodejs
|
RUN apt-get update && apt-get install -y nodejs
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY ["Birdmap.API/Birdmap.API.csproj", "Birdmap.API/"]
|
COPY ["Birdmap.API/Birdmap.API.csproj", "Birdmap.API/"]
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
using Birdmap.API.Options;
|
|
||||||
using Birdmap.API.Services.Mqtt;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Birdmap.API.Extensions
|
|
||||||
{
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action<AspCoreMqttClientOptions> configureOptions)
|
|
||||||
{
|
|
||||||
services.AddSingleton(serviceProvider =>
|
|
||||||
{
|
|
||||||
var optionBuilder = new AspCoreMqttClientOptions(serviceProvider);
|
|
||||||
configureOptions(optionBuilder);
|
|
||||||
return optionBuilder.Build();
|
|
||||||
});
|
|
||||||
services.AddSingleton<MqttClientService>();
|
|
||||||
services.AddSingleton<IHostedService>(serviceProvider =>
|
|
||||||
{
|
|
||||||
return serviceProvider.GetService<MqttClientService>();
|
|
||||||
});
|
|
||||||
services.AddSingleton(serviceProvider =>
|
|
||||||
{
|
|
||||||
var mqttClientService = serviceProvider.GetService<MqttClientService>();
|
|
||||||
var mqttClientServiceProvider = new MqttClientServiceProvider(mqttClientService);
|
|
||||||
return mqttClientServiceProvider;
|
|
||||||
});
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Birdmap.API.Extensions;
|
|
||||||
using Birdmap.API.Middlewares;
|
using Birdmap.API.Middlewares;
|
||||||
using Birdmap.API.Services.Hubs;
|
|
||||||
using Birdmap.BLL;
|
using Birdmap.BLL;
|
||||||
|
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||||
using Birdmap.DAL;
|
using Birdmap.DAL;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
@ -42,8 +41,6 @@ namespace Birdmap.API
|
|||||||
|
|
||||||
services.AddAutoMapper(typeof(Startup));
|
services.AddAutoMapper(typeof(Startup));
|
||||||
|
|
||||||
services.AddSignalR();
|
|
||||||
|
|
||||||
var key = Encoding.ASCII.GetBytes(Configuration["Secret"]);
|
var key = Encoding.ASCII.GetBytes(Configuration["Secret"]);
|
||||||
services.AddAuthentication(opt =>
|
services.AddAuthentication(opt =>
|
||||||
{
|
{
|
||||||
@ -64,33 +61,6 @@ namespace Birdmap.API
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddMqttClientServiceWithConfig(opt =>
|
|
||||||
{
|
|
||||||
var mqtt = Configuration.GetSection("Mqtt");
|
|
||||||
|
|
||||||
var mqttClient = mqtt.GetSection("ClientSettings");
|
|
||||||
var clientSettings = new
|
|
||||||
{
|
|
||||||
Id = mqttClient.GetValue<string>("Id"),
|
|
||||||
Username = mqttClient.GetValue<string>("Username"),
|
|
||||||
Password = mqttClient.GetValue<string>("Password"),
|
|
||||||
Topic = mqttClient.GetValue<string>("Topic"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var mqttBrokerHost = mqtt.GetSection("BrokerHostSettings");
|
|
||||||
var brokerHostSettings = new
|
|
||||||
{
|
|
||||||
Host = mqttBrokerHost.GetValue<string>("Host"),
|
|
||||||
Port = mqttBrokerHost.GetValue<int>("Port"),
|
|
||||||
};
|
|
||||||
|
|
||||||
opt
|
|
||||||
.WithTopic(clientSettings.Topic)
|
|
||||||
.WithCredentials(clientSettings.Username, clientSettings.Password)
|
|
||||||
.WithClientId(clientSettings.Id)
|
|
||||||
.WithTcpServer(brokerHostSettings.Host, brokerHostSettings.Port);
|
|
||||||
});
|
|
||||||
|
|
||||||
// In production, the React files will be served from this directory
|
// In production, the React files will be served from this directory
|
||||||
services.AddSpaStaticFiles(configuration =>
|
services.AddSpaStaticFiles(configuration =>
|
||||||
{
|
{
|
||||||
|
@ -5,5 +5,50 @@
|
|||||||
"Microsoft": "Warning",
|
"Microsoft": "Warning",
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Kestrel": {
|
||||||
|
"Certificates": {
|
||||||
|
"Default": {
|
||||||
|
"Password": "certpass123",
|
||||||
|
"Path": "C:\\Users\\Ricsi\\AppData\\Roaming\\ASP.NET\\Https\\aspnetapp.pfx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"Secret": "7vj.3KW.hYE!}4u6",
|
||||||
|
// "LocalDbConnectionString": "Data Source=DESKTOP-3600\\SQLEXPRESS;Initial Catalog=birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||||
|
"LocalDbConnectionString": "Data Source=DESKTOP-3600;Initial Catalog=birdmap2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||||
|
"Defaults": {
|
||||||
|
"Services": {
|
||||||
|
"Local Database": "https://localhost:44331/health",
|
||||||
|
"KMLabz Services": "https://birb.k8s.kmlabz.com/devices"
|
||||||
|
},
|
||||||
|
"Users": [
|
||||||
|
{
|
||||||
|
"Name": "admin",
|
||||||
|
"Password": "pass",
|
||||||
|
"Role": "Admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "user",
|
||||||
|
"Password": "pass",
|
||||||
|
"Role": "User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"UseDummyServices": true,
|
||||||
|
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
|
||||||
|
"Mqtt": {
|
||||||
|
"BrokerHostSettings": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 1883
|
||||||
|
},
|
||||||
|
|
||||||
|
"ClientSettings": {
|
||||||
|
"Id": "ASP.NET Core client",
|
||||||
|
"Username": "username",
|
||||||
|
"Password": "password",
|
||||||
|
"Topic": "devices/output"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,42 +6,35 @@
|
|||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Kestrel": {
|
||||||
|
"Certificates": {
|
||||||
|
"Default": {
|
||||||
|
"Password": "",
|
||||||
|
"Path": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Secret": "7vj.3KW.hYE!}4u6",
|
"Secret": "",
|
||||||
// "LocalDbConnectionString": "Data Source=DESKTOP-3600\\SQLEXPRESS;Initial Catalog=birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
"LocalDbConnectionString": "",
|
||||||
//"LocalDbConnectionString": "Data Source=DESKTOP-3600;Initial Catalog=birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
|
||||||
"LocalDbConnectionString": "Data Source=db;Initial Catalog=birdmap;User=sa;Password=RPSsql12345",
|
|
||||||
"Defaults": {
|
"Defaults": {
|
||||||
"Services": {
|
"Services": {
|
||||||
"Local Database": "https://localhost:44331/health",
|
|
||||||
"KMLabz Services": "https://birb.k8s.kmlabz.com/devices"
|
|
||||||
},
|
},
|
||||||
"Users": [
|
"Users": []
|
||||||
{
|
|
||||||
"Name": "admin",
|
|
||||||
"Password": "pass",
|
|
||||||
"Role": "Admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "user",
|
|
||||||
"Password": "pass",
|
|
||||||
"Role": "User"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"UseDummyServices": true,
|
"UseDummyServices": false,
|
||||||
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
|
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
|
||||||
"Mqtt": {
|
"Mqtt": {
|
||||||
"BrokerHostSettings": {
|
"BrokerHostSettings": {
|
||||||
"Host": "localhost",
|
"Host": "",
|
||||||
"Port": 1883
|
"Port": 1883
|
||||||
},
|
},
|
||||||
|
|
||||||
"ClientSettings": {
|
"ClientSettings": {
|
||||||
"Id": "ASP.NET Core client",
|
"Id": "ASP.NET Core client",
|
||||||
"Username": "username",
|
"Username": "",
|
||||||
"Password": "password",
|
"Password": "",
|
||||||
"Topic": "devices/output"
|
"Topic": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
autoReload="true"
|
autoReload="true"
|
||||||
internalLogLevel="Info"
|
internalLogLevel="Info"
|
||||||
internalLogFile="${basedir}Log/internal-nlog.txt"
|
internalLogFile="${basedir}Logs/internal-nlog.txt"
|
||||||
throwConfigExceptions="true">
|
throwConfigExceptions="true">
|
||||||
|
|
||||||
<!-- enable asp.net core layout renderers -->
|
<!-- enable asp.net core layout renderers -->
|
||||||
@ -14,17 +14,17 @@
|
|||||||
<!-- the targets to write to -->
|
<!-- the targets to write to -->
|
||||||
<targets async="true">
|
<targets async="true">
|
||||||
<default-target-parameters xsi:type="File" keepFileOpen="false" maxArchiveFiles="10" archiveAboveSize="1048576"/>
|
<default-target-parameters xsi:type="File" keepFileOpen="false" maxArchiveFiles="10" archiveAboveSize="1048576"/>
|
||||||
<target xsi:type="File" name="allFile" fileName="${basedir}Log/birdmap-all-${shortdate}.log"
|
<target xsi:type="File" name="allFile" fileName="${basedir}Logs/birdmap-all-${shortdate}.log"
|
||||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||||
|
|
||||||
<target xsi:type="File" name="mqttFile" fileName="${basedir}Log/birdmap-mqtt-${shortdate}.log"
|
<target xsi:type="File" name="mqttFile" fileName="${basedir}Logs/birdmap-mqtt-${shortdate}.log"
|
||||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||||
|
|
||||||
<target xsi:type="File" name="hubsFile" fileName="${basedir}Log/birdmap-hubs-${shortdate}.log"
|
<target xsi:type="File" name="hubsFile" fileName="${basedir}Logs/birdmap-hubs-${shortdate}.log"
|
||||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||||
|
|
||||||
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
|
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
|
||||||
<target xsi:type="File" name="ownFile" fileName="${basedir}Log/birdmap-own-${shortdate}.log"
|
<target xsi:type="File" name="ownFile" fileName="${basedir}Logs/birdmap-own-${shortdate}.log"
|
||||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${callsite} - ${message} ${exception:format=tostring} (url: ${aspnet-request-url})(action: ${aspnet-mvc-action})" />
|
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${callsite} - ${message} ${exception:format=tostring} (url: ${aspnet-request-url})(action: ${aspnet-mvc-action})" />
|
||||||
</targets>
|
</targets>
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
|
||||||
|
<PackageReference Include="MQTTnet" Version="3.0.13" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ using MQTTnet.Client.Connecting;
|
|||||||
using MQTTnet.Client.Disconnecting;
|
using MQTTnet.Client.Disconnecting;
|
||||||
using MQTTnet.Client.Receiving;
|
using MQTTnet.Client.Receiving;
|
||||||
|
|
||||||
namespace Birdmap.API.Services
|
namespace Birdmap.BLL.Interfaces
|
||||||
{
|
{
|
||||||
public interface IMqttClientService : IHostedService,
|
public interface IMqttClientService : IHostedService,
|
||||||
IMqttClientConnectedHandler,
|
IMqttClientConnectedHandler,
|
@ -1,7 +1,7 @@
|
|||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Birdmap.API.Options
|
namespace Birdmap.BLL.Options
|
||||||
{
|
{
|
||||||
public class AspCoreMqttClientOptions : MqttClientOptionsBuilder
|
public class AspCoreMqttClientOptions : MqttClientOptionsBuilder
|
||||||
{
|
{
|
@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Birdmap.API.Services.Hubs
|
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||||
{
|
{
|
||||||
public class DevicesHub : Hub<IDevicesHubClient>
|
public class DevicesHub : Hub<IDevicesHubClient>
|
||||||
{
|
{
|
@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Birdmap.API.Services
|
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||||
{
|
{
|
||||||
public record Message(Guid DeviceId, DateTime Date, double Probability);
|
public record Message(Guid DeviceId, DateTime Date, double Probability);
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Birdmap.API.Services.Hubs
|
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||||
{
|
{
|
||||||
public interface IServicesHubClient
|
public interface IServicesHubClient
|
||||||
{
|
{
|
@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Birdmap.API.Services.Hubs
|
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||||
{
|
{
|
||||||
public class ServicesHub : Hub<IServicesHubClient>
|
public class ServicesHub : Hub<IServicesHubClient>
|
||||||
{
|
{
|
@ -1,5 +1,5 @@
|
|||||||
using Birdmap.API.Services.Hubs;
|
using Birdmap.BLL.Interfaces;
|
||||||
using Birdmap.BLL.Interfaces;
|
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MQTTnet;
|
using MQTTnet;
|
||||||
@ -15,7 +15,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace Birdmap.API.Services.Mqtt
|
namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
|
||||||
{
|
{
|
||||||
public class MqttClientService : IMqttClientService
|
public class MqttClientService : IMqttClientService
|
||||||
{
|
{
|
@ -1,4 +1,6 @@
|
|||||||
namespace Birdmap.API.Services.Mqtt
|
using Birdmap.BLL.Interfaces;
|
||||||
|
|
||||||
|
namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
|
||||||
{
|
{
|
||||||
public class MqttClientServiceProvider
|
public class MqttClientServiceProvider
|
||||||
{
|
{
|
@ -1,7 +1,11 @@
|
|||||||
using Birdmap.BLL.Interfaces;
|
using Birdmap.BLL.Interfaces;
|
||||||
|
using Birdmap.BLL.Options;
|
||||||
using Birdmap.BLL.Services;
|
using Birdmap.BLL.Services;
|
||||||
|
using Birdmap.BLL.Services.CommunicationServices.Mqtt;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Birdmap.BLL
|
namespace Birdmap.BLL
|
||||||
@ -37,6 +41,57 @@ namespace Birdmap.BLL
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
services.AddSignalR();
|
||||||
|
|
||||||
|
services.AddMqttClientServiceWithConfig(opt =>
|
||||||
|
{
|
||||||
|
var mqtt = configuration.GetSection("Mqtt");
|
||||||
|
|
||||||
|
var mqttClient = mqtt.GetSection("ClientSettings");
|
||||||
|
var clientSettings = new
|
||||||
|
{
|
||||||
|
Id = mqttClient.GetValue<string>("Id"),
|
||||||
|
Username = mqttClient.GetValue<string>("Username"),
|
||||||
|
Password = mqttClient.GetValue<string>("Password"),
|
||||||
|
Topic = mqttClient.GetValue<string>("Topic"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var mqttBrokerHost = mqtt.GetSection("BrokerHostSettings");
|
||||||
|
var brokerHostSettings = new
|
||||||
|
{
|
||||||
|
Host = mqttBrokerHost.GetValue<string>("Host"),
|
||||||
|
Port = mqttBrokerHost.GetValue<int>("Port"),
|
||||||
|
};
|
||||||
|
|
||||||
|
opt
|
||||||
|
.WithTopic(clientSettings.Topic)
|
||||||
|
.WithCredentials(clientSettings.Username, clientSettings.Password)
|
||||||
|
.WithClientId(clientSettings.Id)
|
||||||
|
.WithTcpServer(brokerHostSettings.Host, brokerHostSettings.Port);
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action<AspCoreMqttClientOptions> configureOptions)
|
||||||
|
{
|
||||||
|
services.AddSingleton(serviceProvider =>
|
||||||
|
{
|
||||||
|
var optionBuilder = new AspCoreMqttClientOptions(serviceProvider);
|
||||||
|
configureOptions(optionBuilder);
|
||||||
|
return optionBuilder.Build();
|
||||||
|
});
|
||||||
|
services.AddSingleton<MqttClientService>();
|
||||||
|
services.AddSingleton<IHostedService>(serviceProvider =>
|
||||||
|
{
|
||||||
|
return serviceProvider.GetService<MqttClientService>();
|
||||||
|
});
|
||||||
|
services.AddSingleton(serviceProvider =>
|
||||||
|
{
|
||||||
|
var mqttClientService = serviceProvider.GetService<MqttClientService>();
|
||||||
|
var mqttClientServiceProvider = new MqttClientServiceProvider(mqttClientService);
|
||||||
|
return mqttClientServiceProvider;
|
||||||
|
});
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
MQTTnet.TestApp.WinForm/Retained.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"Topic":"devices/output","Payload":"eyJ0YWciOiJkNDhhODAwOC0wYjU2LTRkYzAtODYxMy0zMWY0MDY4OTk0ZDciLCJwcm9iYWJpbGl0eSI6MC4yNTMxNDI4NDgyNjMwOTU1fQ==","QualityOfServiceLevel":1,"Retain":true,"UserProperties":null,"ContentType":null,"ResponseTopic":null,"PayloadFormatIndicator":null,"MessageExpiryInterval":null,"TopicAlias":null,"CorrelationData":null,"SubscriptionIdentifiers":null}]
|
@ -21,11 +21,12 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
environment:
|
environment:
|
||||||
- ASPNETCORE_ENVIRONMENT=Development
|
- ASPNETCORE_ENVIRONMENT=Docker
|
||||||
- ASPNETCORE_URLS=https://+;http://+
|
- ASPNETCORE_URLS=https://+;http://+
|
||||||
- ASPNETCORE_HTTPS_PORT=8001
|
- ASPNETCORE_HTTPS_PORT=8001
|
||||||
- ASPNETCORE_Kestrel__Certificates__Default__Password=certpass123
|
- Birdmap_Kestrel__Certificates__Default__Password=certpass123
|
||||||
- ASPNETCORE_Kestrel__Certificates__Default__Path=/root/.aspnet/https/aspnetapp.pfx
|
- Birdmap_Kestrel__Certificates__Default__Path=/root/.aspnet/https/aspnetapp.pfx
|
||||||
|
- Birdmap_Secret=7gz;]=bQe}n#3~RwC+Y<SrjoE:sHwO
|
||||||
- Birdmap_LocalDbConnectionString=Data Source=db;Initial Catalog=birdmap;User=sa;Password=RPSsql12345
|
- Birdmap_LocalDbConnectionString=Data Source=db;Initial Catalog=birdmap;User=sa;Password=RPSsql12345
|
||||||
- Birdmap_Defaults__Users__0__Name=admin
|
- Birdmap_Defaults__Users__0__Name=admin
|
||||||
- Birdmap_Defaults__Users__0__Password=pass
|
- Birdmap_Defaults__Users__0__Password=pass
|
||||||
@ -33,7 +34,8 @@ services:
|
|||||||
- Birdmap_Defaults__Users__1__Name=user
|
- Birdmap_Defaults__Users__1__Name=user
|
||||||
- Birdmap_Defaults__Users__1__Password=pass
|
- Birdmap_Defaults__Users__1__Password=pass
|
||||||
- Birdmap_Defaults__Users__1__Role=User
|
- Birdmap_Defaults__Users__1__Role=User
|
||||||
- Birdmap_Defaults__Services__Local-Database=https://localhost/health
|
- Birdmap_Defaults__Services__Local-Database=https://localhost:8001/health
|
||||||
|
- Birdmap_Defaults__Services__KMLabz-Service=https://birb.k8s.kmlabz.com/devices
|
||||||
- Birdmap_UseDummyServices=true
|
- Birdmap_UseDummyServices=true
|
||||||
- Birdmap_ServicesBaseUrl=https://birb.k8s.kmlabz.com/
|
- Birdmap_ServicesBaseUrl=https://birb.k8s.kmlabz.com/
|
||||||
- Birdmap_Mqtt__BrokerHostSettings__Host=localhost
|
- Birdmap_Mqtt__BrokerHostSettings__Host=localhost
|
||||||
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 78 KiB |
1
docs/mqtt-communication-sequence.drawio
Normal file
@ -0,0 +1 @@
|
|||||||
|
<mxfile host="app.diagrams.net" modified="2020-12-03T11:07:06.295Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" etag="ikHYmKMKhruEys4yzSz3" version="13.10.6" type="device"><diagram id="kgpKYQtTHZ0yAKxKKP6v" name="Page-1">5Vpbb6M4FP41SLMPjcBcUh6TtJOt1Gpmt5U62zcnOIk1DkbGNGF+/RpwAGNI6DZ0205f6nNsbHy+79xQDHu23c8ZjDZ3NEDEAGawN+wrAwDLunTFv0yTFhrPsQvFmuFALqoU9/gXkkpTahMcoFhZyCklHEeqcknDEC25ooOM0Z26bEWJemoE10hT3C8h0bWPOOCbQnvpmpX+T4TXm8PJlilntvCwWCriDQzorqayrw17xijlxWi7nyGSGe9gl+K5rx2z5YsxFPI+D9hhMv26Z/4yvTWj8NvjA4DzC7nLMySJvPAUsyBEXNgceAawBQ72dHIj1twj9oyXSF6GpwcLiXtF2TDZklu8QgSHQppGiOGt2IaJGSLV3yvdVODFodBl81YuEwKjGC/ybbNDGVomLMbP6G8UF7TItTQJAxRIqbRpLnBGf5YoZZvqJjrcFzGO9jWVNNkcUfGCLBVL5OylRC9VxV3FhRLxTY0H5UIo+bcud64gEgOJ0gsQAxpiGiIRxSHPz3WnhnvVQIMyvqFrGkJSx+OVdj1Krt7GdlzF2OM2Y7fYeihTu63OsYVRzTXu/np4+G2dwwYqYP+/d3gfxzvcl1rb6WFs3daWdwZb36Br/rR6fCK/5k/2+JuNJ3N4YTmnjZ3dDotsOiF4HQrVgnJOt8JIKAwmWXrOdIQuf542K9pj/iMbjyzHkfI/2eqRK6WrvXw4F9KaUMMy1xXviQIt9/fDLKYJW6LTNOSQrRE/ZsJxO+IMEciFd6vlTAuA8tHvGacrpgAHjDLPK/8chTlialSbNBv7F7eTW9YLisYplq3mRtBkWnF9bSOBO0xry6RDdt6mZHCqyp3v1VgPTGW9GBRvUDG/tPwrnEFPFiJLEAH+dMHEaM1L2tXcI2PgLVyImtmebviWSKpD6S1LwcY8/jTdaIuDINtD5AER/mGVF/Tg1hFlyopZPmyUabTuBEf8vjNMXZiCe56nQlBI/5XWhyV0tYoRb4B5Hvj0xAGD4IFOk9UKMR3JLwHK8v1NYIBZ1t1AjopRxOgCLjDBPP3jd4HbdsBZ4S7jy8j16nEKqIcAdcMByTF+N1VFC7D9klYnfm57sDxVVthDVXCXmq2jZEFwvDFmwJiIU833P/iCgx7R4Iy1kc4Eq2elAvRK5Vjl0yiZzl+5jNUiV6te+xYnJWm7NuooTs4WMzQat31YuQmjhH/k9vFEPDre0LhAxehN28dW1N7Px5UThu3mXN/20e8X552hTG1rpi4+pTQrrVi4RlaBNUspQnAUo8KcuY8sCU3EgdPdBnN0H8E87O0YjNRi6wy8tRofBS3QYkvQYszBeNvjs8cgnbjpXyqduOkd5s/ai/dNZr3b7kFyl9NIOWUp9dLc5XbVZG+Uu/wWNn3OVtY76ucfs5M1NfQEV+4Qh6Iq/SQt6UnYBupIgTMCvrLvoV15A1z1inLQGB+K1/2RB3XfP8hFkLetckEV5XMprUvNOH/IGgIe0/KVtOH7zhuljWNV38keqIN2rySV20wbzSqhb9rwrPForNJTlHmjselXfw26Dt0EtdXTnzOTWMdLxo+ZSvQanSGesDD7xKF/AP0kyeU0kgNllwvbG5ljNb1Y1sgFr08xQqx+aFIsr36uY1//Cw==</diagram></mxfile>
|
68
docs/thesis/Makefile
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
DOCUMENT=thesis
|
||||||
|
#MODE=-interaction=batchmode
|
||||||
|
|
||||||
|
all: clean xelatex
|
||||||
|
echo
|
||||||
|
|
||||||
|
xelatex: compile_xelatex
|
||||||
|
mv $(DOCUMENT)-xelatex.pdf ../pdf/$(DOCUMENT).pdf
|
||||||
|
|
||||||
|
compile_xelatex:
|
||||||
|
xelatex $(MODE) $(DOCUMENT)
|
||||||
|
bibtex $(DOCUMENT)
|
||||||
|
xelatex $(MODE) $(DOCUMENT)
|
||||||
|
xelatex $(MODE) $(DOCUMENT)
|
||||||
|
mv $(DOCUMENT).pdf $(DOCUMENT)-xelatex.pdf
|
||||||
|
|
||||||
|
pdflatex: compile_pdflatex
|
||||||
|
mv $(DOCUMENT)-pdflatex.pdf ../pdf/$(DOCUMENT).pdf
|
||||||
|
|
||||||
|
compile_pdflatex:
|
||||||
|
pdflatex $(MODE) $(DOCUMENT)
|
||||||
|
bibtex $(DOCUMENT)
|
||||||
|
pdflatex $(MODE) $(DOCUMENT)
|
||||||
|
pdflatex $(MODE) $(DOCUMENT)
|
||||||
|
mv $(DOCUMENT).pdf $(DOCUMENT)-pdflatex.pdf
|
||||||
|
|
||||||
|
lualatex: compile_lualatex
|
||||||
|
mv $(DOCUMENT)-lualatex.pdf ../pdf/$(DOCUMENT).pdf
|
||||||
|
|
||||||
|
compile_lualatex:
|
||||||
|
lualatex $(MODE) $(DOCUMENT)
|
||||||
|
bibtex $(DOCUMENT)
|
||||||
|
lualatex $(MODE) $(DOCUMENT)
|
||||||
|
lualatex $(MODE) $(DOCUMENT)
|
||||||
|
mv $(DOCUMENT).pdf $(DOCUMENT)-lualatex.pdf
|
||||||
|
|
||||||
|
switch_to_hungarian:
|
||||||
|
sed -i "s|^\\\input{include/thesis-en}|%\\\input{include/thesis-en}|" $(DOCUMENT).tex
|
||||||
|
sed -i "s|^%\\\input{include/thesis-hu}|\\\input{include/thesis-hu}|" $(DOCUMENT).tex
|
||||||
|
|
||||||
|
test_hu:
|
||||||
|
${MAKE} clean compile_xelatex
|
||||||
|
${MAKE} clean compile_pdflatex
|
||||||
|
${MAKE} clean compile_lualatex
|
||||||
|
mv $(DOCUMENT)-xelatex.pdf ../pdf/$(DOCUMENT)-xelatex-hu.pdf
|
||||||
|
mv $(DOCUMENT)-pdflatex.pdf ../pdf/$(DOCUMENT)-pdflatex-hu.pdf
|
||||||
|
mv $(DOCUMENT)-lualatex.pdf ../pdf/$(DOCUMENT)-lualatex-hu.pdf
|
||||||
|
|
||||||
|
switch_to_english:
|
||||||
|
sed -i "s|^\\\input{include/thesis-hu}|%\\\input{include/thesis-hu}|" $(DOCUMENT).tex
|
||||||
|
sed -i "s|^%\\\input{include/thesis-en}|\\\input{include/thesis-en}|" $(DOCUMENT).tex
|
||||||
|
|
||||||
|
test_en:
|
||||||
|
${MAKE} switch_to_english
|
||||||
|
${MAKE} clean compile_xelatex
|
||||||
|
${MAKE} clean compile_pdflatex
|
||||||
|
${MAKE} clean compile_lualatex
|
||||||
|
mv $(DOCUMENT)-xelatex.pdf ../pdf/$(DOCUMENT)-xelatex-en.pdf
|
||||||
|
mv $(DOCUMENT)-pdflatex.pdf ../pdf/$(DOCUMENT)-pdflatex-en.pdf
|
||||||
|
mv $(DOCUMENT)-lualatex.pdf ../pdf/$(DOCUMENT)-lualatex-en.pdf
|
||||||
|
${MAKE} switch_to_hungarian
|
||||||
|
|
||||||
|
test: test_hu test_en
|
||||||
|
echo
|
||||||
|
|
||||||
|
clean:
|
||||||
|
echo Cleaning temporary files...
|
||||||
|
rm -f *.aux *.dvi *.thm *.lof *.log *.lot *.fls *.out *.toc *.bbl *.blg
|
102
docs/thesis/bib/mybib.bib
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
@misc{kubernetes,
|
||||||
|
title = {A Kubernetes hivatalos oldala},
|
||||||
|
url = {https://kubernetes.io},
|
||||||
|
}
|
||||||
|
|
||||||
|
@dashboard{kubernetes-dashboard,
|
||||||
|
title = {A Kubernetes Dashboard hivatalos oldala},
|
||||||
|
url = {https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{docker,
|
||||||
|
title = {A Docker hivatalos oldala},
|
||||||
|
url = {https://www.docker.com},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{grafana,
|
||||||
|
title = {A Grafana hivatalos oldala},
|
||||||
|
url = {https://grafana.com/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{kibana,
|
||||||
|
title = {A Kibana hivatalos oldala},
|
||||||
|
url = {https://www.elastic.co/kibana},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{git,
|
||||||
|
title = {A Git hivatalos oldala},
|
||||||
|
url = {https://git-scm.com/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{trello,
|
||||||
|
title = {A Trello hivatalos oldala},
|
||||||
|
url = {https://trello.com},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{vs,
|
||||||
|
title = {A Microsoft Visual Studio hivatalos oldala},
|
||||||
|
url = {https://visualstudio.microsoft.com/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{vs-code,
|
||||||
|
title = {A Microsoft Visual Studio Code hivatalos oldala},
|
||||||
|
url = {https://code.visualstudio.com/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{nlog,
|
||||||
|
title = {Az NLog hivatalos oldala},
|
||||||
|
url = {https://nlog-project.org/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{jwt,
|
||||||
|
title = {Az JSON Web Token hivatalos oldala},
|
||||||
|
url = {https://jwt.io/introduction/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{react,
|
||||||
|
title = {A React.js hivatalos oldala},
|
||||||
|
url = {https://reactjs.org/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{material,
|
||||||
|
title = {A Material hivatalos oldala},
|
||||||
|
url = {https://material.io/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{material-ui,
|
||||||
|
title = {A Material UI hivatalos oldala},
|
||||||
|
url = {https://material-ui.com/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{apexcharts,
|
||||||
|
title = {Az Apexcharts hivatalos oldala},
|
||||||
|
url = {https://apexcharts.com/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{google-map-react,
|
||||||
|
title = {A Google Map React hivatalos oldala},
|
||||||
|
url = {https://www.npmjs.com/package/google-map-react},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{nswag,
|
||||||
|
title = {Az NSwag github oldala},
|
||||||
|
url = {https://github.com/RicoSuter/NSwag},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{swagger-ui,
|
||||||
|
title = {A Swagger UI hivatalos oldala},
|
||||||
|
url = {https://swagger.io/tools/swagger-ui/},
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{hmacsha512,
|
||||||
|
title = {Az HMACSHA512 dokumentációja},
|
||||||
|
url = {https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.hmacsha512},
|
||||||
|
}
|
||||||
|
|
||||||
|
@thesis{birdnetes-tdk,
|
||||||
|
author = {Torma Kristóf és Pünkösdi Marcell},
|
||||||
|
institution = {Budapesti Műszaki és Gazdaságtudományi Egyetem},
|
||||||
|
title = {Madárhang azonosító és riasztó felhő-natív rendszer},
|
||||||
|
type = {tdk},
|
||||||
|
year = {2020},
|
||||||
|
}
|
47
docs/thesis/content/abstract.aux
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {chapter}{Kivonat}{i}{chapter*.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{english} \contentsline {chapter}{Abstract}{ii}{chapter*.3}\protected@file@percent }
|
||||||
|
\@setckpt{content/abstract}{
|
||||||
|
\setcounter{page}{3}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{0}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{0}
|
||||||
|
\setcounter{section}{0}
|
||||||
|
\setcounter{subsection}{0}
|
||||||
|
\setcounter{subsubsection}{0}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{0}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{0}
|
||||||
|
\setcounter{bookmark@seq@number}{2}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{2}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{0}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
\setcounter{romanPage}{3}
|
||||||
|
}
|
63
docs/thesis/content/abstract.tex
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
\pagenumbering{roman}
|
||||||
|
\setcounter{page}{1}
|
||||||
|
|
||||||
|
\selecthungarian
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
% Abstract in Hungarian
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter*{Kivonat}\addcontentsline{toc}{chapter}{Kivonat}
|
||||||
|
|
||||||
|
Adott egy tanszéken fejlesztett felhő alapú elosztott rendszer, melynek eszközei madárhangok azonosítására képesek.
|
||||||
|
Ha a rendszer úgy észleli, hogy az egyik álatala vezérelt eszköz mikrofonja felvételén madárhang található,
|
||||||
|
akkor riasztást kezdeményez az eszközön ezzel elijesztve a madarat ezáltal megóvva a növényzetet.
|
||||||
|
|
||||||
|
A rendszernek több kisebb komponense van, amelyek rengeteg adatot dolgoznak fel és nincs jelenleg egy olyan egységes grafikus felület ahol a rendszer teljes állapotát
|
||||||
|
át lehetne tekinteni, ahol a feldolgozott adatokat vizualizálni lehetne.
|
||||||
|
|
||||||
|
A piacon létezik már több olyan szoftver csomag, amely hasonló problémákra próbál megoldást nyújtani, de ezek sem mindig
|
||||||
|
tudják kielégíteni azokat a speciális igényeket, amelyek egy ilyen rendszernél felmerülnek.
|
||||||
|
|
||||||
|
Jelen szakdolgozat célja egy olyan vizualizációs megoldás bemutatása, amelynek segítségével a rendszer könnyedén áttekinthető
|
||||||
|
és kezelhető. A tanszéki rendszer által kezelt eszközök a felületen is vezérelhetők
|
||||||
|
és azok működéséről különböző statisztikákat felhasználva egyszerűen értelmezhető diagrammok generálódnak.
|
||||||
|
|
||||||
|
A backend megvalósítására az ASP.NET Core-t választottam, mely platformfüggetlen megoldást nyújt a web kérések kiszolgálására.
|
||||||
|
A frontend-et a React.js használatával készítettem, mely segítségével egyszerűen és gyorsan lehet reszponzív felhasználói felületeket készíteni.
|
||||||
|
Dolgozatomban bemutatom a tanszéken fejlesztett rendszert, a mikroszolgáltatások vizualizálásának alternatíváit,
|
||||||
|
ismertetem az általam választott technológiákat és a készített alkalmazás felépítését.
|
||||||
|
|
||||||
|
\vfill
|
||||||
|
\selectenglish
|
||||||
|
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
% Abstract in English
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter*{Abstract}\addcontentsline{toc}{chapter}{Abstract}
|
||||||
|
|
||||||
|
There is a department developed cloud-based distributed system whose devices are capable of identifying bird sounds.
|
||||||
|
If the system detects a bird's voice on the recording of a microphone on one of the devices, it will trigger
|
||||||
|
an alarm on the device scaring the bird away thereby protecting the vegetation.
|
||||||
|
|
||||||
|
The system has several smaller components that process a lot of data and currently there is no unified graphical user interface where the overall state of the system
|
||||||
|
could be reviewed, where the processed data could be visualized.
|
||||||
|
|
||||||
|
There are already several software packages on the market that try to solve similar problems,
|
||||||
|
however they aren't always able to meet the special needs that arise with such a system.
|
||||||
|
|
||||||
|
The purpose of this thesis is to present a visualization solution that allows the users to easily review
|
||||||
|
and manage the system. The devices maintained by the department developed system can be controlled on the interface
|
||||||
|
and easy-to-understand diagrams are generated using statistics about their operation.
|
||||||
|
|
||||||
|
I chose ASP.NET Core as the backend framework, which provides a platform-independent solution for serving web requests.
|
||||||
|
The frontend was created using React.js, which allows for an easy and quick way to create responsive user interfaces.
|
||||||
|
In my thesis I present the system developed at the department, the alternatives of visualization of microservices,
|
||||||
|
I describe the technologies I have chosen and the structure of the application I have created.
|
||||||
|
|
||||||
|
\vfill
|
||||||
|
\selectthesislanguage
|
||||||
|
|
||||||
|
\newcounter{romanPage}
|
||||||
|
\setcounter{romanPage}{\value{page}}
|
||||||
|
\stepcounter{romanPage}
|
32
docs/thesis/content/appendices.tex
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\appendix
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter*{\fuggelek}\addcontentsline{toc}{chapter}{\fuggelek}
|
||||||
|
\setcounter{chapter}{\appendixnumber}
|
||||||
|
%\setcounter{equation}{0} % a fofejezet-szamlalo az angol ABC 6. betuje (F) lesz
|
||||||
|
\numberwithin{equation}{section}
|
||||||
|
\numberwithin{figure}{section}
|
||||||
|
\numberwithin{lstlisting}{section}
|
||||||
|
%\numberwithin{tabular}{section}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{A TeXstudio felülete}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/TeXstudio.png}
|
||||||
|
\caption{A TeXstudio \LaTeX-szerkesztő.}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\clearpage\section{Válasz az ,,Élet, a világmindenség, meg minden'' kérdésére}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Pitagorasz-tételből levezetve
|
||||||
|
\begin{align}
|
||||||
|
c^2=a^2+b^2=42.
|
||||||
|
\end{align}
|
||||||
|
A Faraday-indukciós törvényből levezetve
|
||||||
|
\begin{align}
|
||||||
|
\rot E=-\frac{dB}{dt}\hspace{1cm}\longrightarrow \hspace{1cm}
|
||||||
|
U_i=\oint\limits_\mathbf{L}{\mathbf{E}\mathbf{dl}}=-\frac{d}{dt}\int\limits_A{\mathbf{B}\mathbf{da}}=42.
|
||||||
|
\end{align}
|
15
docs/thesis/content/birdmap-backend.aux
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {chapter}{\numberline {5}Szerver oldal}{14}{chapter.5}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\@writefile{lot}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\newlabel{chapt:birdmap-backend}{{5}{14}{Google Maps Api}{chapter.5}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {5.1}Architektúra}{14}{section.5.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {5.2}Adat elérési réteg}{14}{section.5.2}\protected@file@percent }
|
||||||
|
\citation{nswag}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {5.2.1}Entitások}{15}{subsection.5.2.1}\protected@file@percent }
|
||||||
|
\@writefile{lol}{\select@language{magyar} \contentsline {lstlisting}{\numberline {5.1}A User és a Service modell}{15}{lstlisting.5.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {5.2.2}Seedelés}{15}{subsection.5.2.2}\protected@file@percent }
|
||||||
|
\newlabel{subsect:seeding}{{5.2.2}{15}{Seedelés}{subsection.5.2.2}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {5.3}Üzleti logikai réteg}{15}{section.5.3}\protected@file@percent }
|
||||||
|
\citation{hmacsha512}
|
189
docs/thesis/content/birdmap-backend.tex
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter{Szerver oldal}
|
||||||
|
\label{chapt:birdmap-backend}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a fejezetben bemutatom a szerveroldal architektúráját, felépítését. Ismertetem a különböző szoftver komponensek feladatát.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Architektúra}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A szerveroldal fejlesztésénél a háromrétegú architektúrát alkalmaztam, melynek lényege, hogy az alkalmazást logikailag három elkülönülő részre bontjuk:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Adat elérési réteg}. Ez a rész felel a tárolt entitások modell definícióiért, illetve azoknak a kiolvasásáért, tárolásáért egy adatbázisból vagy fájlrendszerből.
|
||||||
|
\item \textbf{Megjelenítési réteg}. Ezen réteg feladata a kliensoldal közvetlek kiszolgálása. Bármilyen irányú kommunikáció a kliensek felé ezen a rétegen keresztül történik.
|
||||||
|
\item \textbf{Üzleti logikai réteg}. Minden ami nem a közvetlen kommunikációért, megjelenítésért vagy adat elérésért, tárolásért felel, az ide kerül.
|
||||||
|
A fenti két réteg között helyezkedik el és feladata a különböző folyamatok értékelése és futtatása, valamint az adatok feldolgozása.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Az ASP.NET Core beépítetten támogatja a dependency injection-t, mely a \verb+Startup+ osztály \verb+ConfigureServices+ metódusával konfigurálható.
|
||||||
|
Én minden rétegbe tettem egy ilyen \verb+Startup+ osztályt, hogy azok feleljenek a saját szolgáltatásaik konfigurálásáért és regisztrálásáért.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Adat elérési réteg}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az adatelérést az Entity Framework Core segítségével oldottam meg. Telepítettem egy MSSQL adatbázis szervert a számítógépemre, melynek csatlakozási paramétereivel
|
||||||
|
a \verb+Startup+ osztályban felkonfigurálom az EF Core által nyújtott \verb+DbContext+ saját leszármazott változatát.
|
||||||
|
Így csak az entitások elkészítése és azok alapértelmezett értékeinek az adatbázisba való feltöltése marad hátra.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Entitások}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Mivel az adatok nagy részét külső szolgáltatások fogják nyújtani, így lokálisan összesen két entitás létrehozására volt szükség.
|
||||||
|
Az egyik a \verb+User+, mely az alkalmazás felhasználóinak adatait tárolja.
|
||||||
|
A másik a \verb+Service+, mely a külső szolgáltatások adatainak tárolását szolgálja, amelyeket azért tárolok az adatbázisban és nem mondjuk a konfigurációs fájlban,
|
||||||
|
mert szerettem volna, hogyha a kezelőfelületen lehetne őket szerkeszteni, törölni.
|
||||||
|
|
||||||
|
\lstset{style=sharpc, morekeywords={record, get, set}}
|
||||||
|
\begin{lstlisting}[caption=A User és a Service modell]
|
||||||
|
public record User
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public byte[] PasswordHash { get; set; }
|
||||||
|
public byte[] PasswordSalt { get; set; }
|
||||||
|
|
||||||
|
public Roles Role { get; set; }
|
||||||
|
|
||||||
|
public bool IsFromConfig { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Service
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Uri Uri { get; set; }
|
||||||
|
|
||||||
|
public bool IsFromConfig { get; set; }
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Az alkalmazás használata szempontjából a felhasználók két csoportba oszlanak.
|
||||||
|
Vannak adminisztrátor és sima felhasználók, utóbbi csak az adatok olvasására, míg előbb azok módosítására is jogosult.
|
||||||
|
A \verb+Role+ mező ennek a megkülönböztetsnek a jelzője.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Seedelés}
|
||||||
|
\label{subsect:seeding}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az alkalmazás konfigurációs fájljából meg lehet adni alapértelmezett felhasználókat és szolgáltatásokat.
|
||||||
|
Ezeknek megkülönböztetésére szolgál az entitások \verb+IsFromConfig+ mezője.
|
||||||
|
A szerver indítása legelején, megvizsgálja, hogy létezik-e az adatbázis és ha igen kitöröl minden olyan entitást ahol az \verb+IsFromConfig+ mező igaz.
|
||||||
|
Majd hozzáadja az újonnan beolvasott értékeket.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Üzleti logikai réteg}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a rétegben található meg a szerver legtöbb szolgáltatása. It vannak implementálva a Birdnetes Command and Control és Input komponensekkel kommunikáló szolgáltatások is,
|
||||||
|
melyeket azok OpenAPI leírói alapján az NSwag\cite{nswag} alkalmazással generáltam. Az OpenAPI a klienseken kívül definiálja még az azok által használt modelleket is.
|
||||||
|
A Command and Control által használt \verb+Device+ modell tartalmazza annak egyedi azonosítóját, státuszát, koordinátáit és a használt szenzorok listáját,
|
||||||
|
melyeknek szintén van egy modellje \verb+Sensor+ néven. Ennek szintén van azonosítója és státusza. Az Input szolgáltatásnak is van saját modellje,
|
||||||
|
amely a hangüzenetek metaadatait reprezentálja. Többek között tartalmazza a kihelyezett eszköz egyedi azonosítóját és a hangüzenet keltének dátumát.
|
||||||
|
|
||||||
|
Ugyan itt található meg a \verb+User+ és \verb+Service+ entitások létrehozásáért, olvasásáért, szerkesztéséért és törléséért felelős szolgáltatások is.
|
||||||
|
Valamint itt található még az autentikációért felelős szolgáltatás is. A felhasználók jelszavainak tárolására a HMAC (Hash-based Message Authentication Code) algorithmust,
|
||||||
|
pontosabban annak a \verb+HMACSHA512+\cite{hmacsha512} C\# implementációját használtam.
|
||||||
|
|
||||||
|
Minden jelszóhoz generálok egy egyedi kulcsot és azzal egy hash-t, majd ezeket tárolom a \verb+User+ modell \verb+PasswordSalt+ és \verb+PasswordHash+ mezőiben.
|
||||||
|
Amikor egy felhasználó be akar jelentkezni először megvizsgálom, hogy egyáltalán létezik-e az adatbázisban az adott nevű felhasználó,
|
||||||
|
ha igen, akkor a megadott jelszóból az imént említett folyamattal generált kulcsot és hash-t összehasonlítom az adatbázisban tárolttal.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Kommunikációs Szolgáltatások}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A kliensoldal frissítésére több megoldás is létezik. Például bizonyos időközönként lehetne kéréseket indítani a szerver felé a friss adatok megszerzéséért.
|
||||||
|
Egy másik megoldás a SignalR használata, amellyel a klienseket eseményvezérelten lehet értesíteni, megvalósítja a kétoldalú kommunikációt.
|
||||||
|
Így a kliensek csak akkor indítanak kéréseket amikor az adat tényleg változott. Ezzel a technológiával oldottam meg például, hogy az eszközök állapotainak változására
|
||||||
|
frissüljön a felület.
|
||||||
|
|
||||||
|
Egy másik szerveroldalon használt szolgáltatás a Birdnetes MQTT kommunikációért felelős szolgáltatás,
|
||||||
|
mely felregisztrál a \ref{subsect:birdnetes-ai-service}-as alfejezetben bemutatott AI Service által publikált üzenetekre.
|
||||||
|
Ezekben az üzenetekben található a hanganyagok egyedi azonosítója, illetve azok seregélytől való származásának valószínüsége.
|
||||||
|
Ha a szolgáltatás kap egy ilyen üzenetet akkor lekérdezi a \ref{subsect:birdnetes-input-service}-es alfejezetben bemutatott Input Service-től
|
||||||
|
a hanganyag azonosítójához tartozó metaadatokat.
|
||||||
|
Ezekből felhasználva a kihelyezett eszköz azonosítóját, a hanganyag beérkezésének dátumát és az említett valószínüséget új üzenetek készülnek, melyeket egy pufferben tárolódnak.
|
||||||
|
Ezt a folyamatot a \ref{fig:birdmap-mqtt-service}-es ábra szemlélteti.
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/mqtt-communication-sequence.png}
|
||||||
|
\caption{A Birdmap MQTT szolgáltatásának szekvenciája}
|
||||||
|
\label{fig:birdmap-mqtt-service}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
A puffer tartalmát másodperces gyakorisággal elküldöm a klienseknek a SignalR segítségével.
|
||||||
|
Azért van szükség a puffer használatára, mert az MQTT-n érkezett üzenetek gyakorisága akár miliszekundum nagyságrendű is lehet.
|
||||||
|
Míg a szerver képes is az üzeneteket feldolgozni, ha ezeket rögtön tovább küldeném a kliensek felé, azok nem biztos, hogy képesek lennének rá.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Megjelenítési réteg}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A fejezet elején említett \verb+Startup+ osztály ebben a rétegben található, itt kerülnek az egyes szolgáltatások regisztrálásra.
|
||||||
|
Itt történik a \ref{subsect:seeding} fejezetben leírt adatbázis seedelése is.
|
||||||
|
|
||||||
|
Többek között a naplózás is itt kerül inicializálásra, mely az NLog saját konfigurációs fájljával történik.
|
||||||
|
Meg lehet adni különböző szűrőket és kimeneteket, amellyel szelektálni lehet, hogy az egyes naplózott események hova kerüljenek.
|
||||||
|
Például az MQTT szolgáltalás napló bejegyzéseit a \ref{lst:nlog-config} lista alapján szűrtem.
|
||||||
|
Minden \verb+Debug+ szintől nagyobb és \verb+Error+ szinttől kisebb bejegyzés, mely tartalmazza az \verb+Mqtt+ kulcsszót az \verb+mqttFile+ azonosítójú fájlba kerül.
|
||||||
|
|
||||||
|
\lstset{style=xml, morekeywords={targets, target, xsi:type, name, fileName, layout, rules, logger, name, minlevel, maxlevel, writeTo, final}}
|
||||||
|
\begin{lstlisting}[caption=Az NLog.config fájl egy részlete, label=lst:nlog-config]
|
||||||
|
<targets>
|
||||||
|
...
|
||||||
|
<target xsi:type="File" name="mqttFile" fileName="${basedir}Logs/birdmap-mqtt-${shortdate}.log"
|
||||||
|
layout="..." />
|
||||||
|
...
|
||||||
|
</targets>
|
||||||
|
|
||||||
|
<rules>
|
||||||
|
...
|
||||||
|
<logger name="*.*Mqtt*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
|
||||||
|
...
|
||||||
|
</rules>
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
A \verb+Startup+ osztály másik metódusa a \verb+Configure+, mellyel a HTTP kérések csővezetéke konfigurálható.
|
||||||
|
Azaz, hogy egy kérés-t milyen sorrendben dolgozzák fel a regisztrált szolgáltatások.
|
||||||
|
A szerveroldali kivételkezelésre szánt szolgáltatás, az \verb+ExceptionHandlerMiddleware+ is itt van használva,
|
||||||
|
amely elkap minden kivételt, amit a csővezeték további részei dobtak és JSON formátumban visszaadja azokat a kliensnek.
|
||||||
|
|
||||||
|
Továbbá az NSwag\cite{nswag} szoftvercsomag segítségével regisztrálok egy szolgáltatást,
|
||||||
|
mely a szerveroldalon található kontrollereket felhasználva generál egy OpenAPI specifikációt és annak egy Swagger UI\cite{swagger-ui} felületet,
|
||||||
|
ahol a végpontok kipróbálhatóak, tesztelhetőek kliensoldal nélkül is.
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/swagger-ui.png}
|
||||||
|
\caption{Az alkalmazásom Swagger felülete}
|
||||||
|
\label{fig:swagger-ui}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Kontrollerek}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A kontrollerek határozzák meg, hogy a szerveroldalon milyen végpontokat, milyen paraméterekkel lehet meghívni, ahhoz milyen jogosultságok kellenek.
|
||||||
|
A jogosultságok kezelését a JSON Web Token-ekkel oldottam meg. A fejlasználó bejelentkezéskor kap egy ilyen token-t,
|
||||||
|
amelyben tárolom a hozzá tartozó szerepet. A \ref{lst:devices-controller}-as listában látszik, hogy hogyan használom ezeket a szerepeket.
|
||||||
|
A \verb+DevicesController+ végpontjait alapértelmezetten \verb+User+ és \verb+Admin+ jogosultságú felhasználó hívhatja, az "online" végpontot azonban csak \verb+Admin+ jogosultságú.
|
||||||
|
Hasonló képpen oldottam meg ezt a többi kontrollernél is. A \verb+User+ felhasználók csak olyan végpontokat hívhat, mely kizárolag az állapotok olvasásával jár.
|
||||||
|
Az \verb+Admin+ felhasználók hívhatnak bármilyen végpontot.
|
||||||
|
|
||||||
|
\lstset{style=sharpc, morekeywords={record, async}}
|
||||||
|
\begin{lstlisting}[caption=Az eszköz kontroller és annak "online" végpontja, label=lst:devices-controller]
|
||||||
|
[Authorize(Roles = "User, Admin")]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class DevicesController : ControllerBase
|
||||||
|
{
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
[HttpPost, Route("online")]
|
||||||
|
public async Task<IActionResult> Onlineall()
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Controllersw
|
||||||
|
Dtos
|
||||||
|
mapper
|
0
docs/thesis/content/birdmap-frontend.tex
Normal file
64
docs/thesis/content/birdmap-introduction.aux
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\citation{kubernetes}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {chapter}{\numberline {3}Tervek és alternatívák}{7}{chapter.3}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\@writefile{lot}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\newlabel{chapt:birdmap-introduction}{{3}{7}{Command and Control Service}{chapter.3}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {3.1}Tervezés}{7}{section.3.1}\protected@file@percent }
|
||||||
|
\citation{grafana}
|
||||||
|
\citation{kibana}
|
||||||
|
\citation{kubernetes-dashboard}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {3.2}Alternatívák}{8}{section.3.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {3.2.1}Grafana}{8}{subsection.3.2.1}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \contentsline {figure}{\numberline {3.1.}{\ignorespaces A Grafana demo oldalának, a \url {https://play.grafana.org}-nak a felülete\relax }}{8}{figure.caption.5}\protected@file@percent }
|
||||||
|
\newlabel{fig:grafana}{{3.1}{8}{A Grafana demo oldalának, a \url {https://play.grafana.org}-nak a felülete\relax }{figure.caption.5}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {3.2.2}Kibana}{8}{subsection.3.2.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {3.2.3}Kubernetes Dashboard (Web UI)}{8}{subsection.3.2.3}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \contentsline {figure}{\numberline {3.2.}{\ignorespaces Egy példa a Kibana kezelőfelületére\relax }}{9}{figure.caption.6}\protected@file@percent }
|
||||||
|
\newlabel{fig:kibana}{{3.2}{9}{Egy példa a Kibana kezelőfelületére\relax }{figure.caption.6}{}}
|
||||||
|
\@writefile{lof}{\select@language{magyar} \contentsline {figure}{\numberline {3.3.}{\ignorespaces A Kubernetes Dashboard felülete\relax }}{9}{figure.caption.7}\protected@file@percent }
|
||||||
|
\newlabel{fig:kibana}{{3.3}{9}{A Kubernetes Dashboard felülete\relax }{figure.caption.7}{}}
|
||||||
|
\@setckpt{content/birdmap-introduction}{
|
||||||
|
\setcounter{page}{10}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{1}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{3}
|
||||||
|
\setcounter{section}{2}
|
||||||
|
\setcounter{subsection}{3}
|
||||||
|
\setcounter{subsubsection}{0}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{3}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{2}
|
||||||
|
\setcounter{bookmark@seq@number}{27}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{4}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{2}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
\setcounter{romanPage}{3}
|
||||||
|
}
|
71
docs/thesis/content/birdmap-introduction.tex
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter{Tervek és alternatívák}
|
||||||
|
\label{chapt:birdmap-introduction}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a fejezetben bemutatom a fejlesztés előtti állapotot, amikor még csak tervezgettük, hogy milyen is legyen az alkalmazás.
|
||||||
|
Illetve bemutatok, néhány vizualizációs alternatívát, melyek jó iránymutatásként szólgálltak a fejlesztés során.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Tervezés}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az első dolgom az volt, hogy Kristóffal és Marcellel beültünk egy Teams\footnotemark-en tartott gyűlésre,
|
||||||
|
ahol elmagyarázták nagyvonalakban, hogy hogyan is működik a rendszer, mik az egyes kompnensek feladatai.
|
||||||
|
Ezek után az előttem álló fejlesztésre váró alkalmazás részleteit beszéltük meg, az elvárt igényeket azzal kapcsolatban.
|
||||||
|
Itt rögtön több ötlet is felmerült, melyek közül a legkiemelkedőbbek:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Hőtérkép}. Hasznos lenne egy olyan felület, ahol az eszközök GPS koordinátái és a seregély detektálást jelző üzenetek alapján, meg lehetne jeleníteni a seregélyek hozzávetőleges előfordulásának helyeit és gyakoriságát egy térképen, hőtérképes formában.
|
||||||
|
\item \textbf{Eszköz állapotok}. Jelenleg a Command and Control mikroszolgáltatás felé indított kéréseken kívül, nincs lehetőség a kihelyezett eszközök állapotának vizsgálatára. Szükség lenne egy olyan felületre, ahol ezek állapotai láthatóak, esetleg dinamikusan is frissülnek.
|
||||||
|
\item \textbf{Diagrammok}. A hőtérképen kívül egyéb olyan diagrammok is hasznosak lehetnek, ahol látható például, hogy melyik eszköz melyik percben észlelt madárhangot vagy, hogy egy eszköz összesen hány madárhangot észelt. Minnél több információ, annál jobb.
|
||||||
|
\end{itemize}
|
||||||
|
Ezeken kívül fontos követelmény volt még, hogy az alkalmazásom futtatható legyen Linux környezetben is, hogy az telepíthető legyen a Birdnetes Kubernetes\cite{kubernetes} klaszterébe.
|
||||||
|
|
||||||
|
Az alkalmazásom kapott egy nevet is, mely a Birdnetes-t és az említett hőtérképes ötletet ötvözve Birdmap lett.
|
||||||
|
\footnotetext{Microsoft Teams: Csevegő és gyülekezés tartó alkalmazás.}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Alternatívák}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az imént vázolt igények kielégítésére rengeteg kiforrott megoldás létezik már, melyek jó példát mutattak a saját alkalmazásom fejlesztése során.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Grafana}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Grafana\cite{grafana} az egy nyílt forráskódú platformfüggetlen vizualizációs web alkalmazás.
|
||||||
|
Egy támogatott adatbázishoz csatlakoztatva különféle interaktív gráfokat és diagrammokat generál.
|
||||||
|
A testreszabhatóság maximalizásának érdekében különböző, akár harmadik fél által készített, bővítmények használatát is támogatja,
|
||||||
|
melyekkel új adatforrások és panel típusok integrálhatók.
|
||||||
|
A \ref{fig:grafana}-es ábra egy jó példa arra, hogy hogyan néz ki egy általános Grafana felület.
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/grafana.png}
|
||||||
|
\caption{A Grafana demo oldalának, a \url{https://play.grafana.org}-nak a felülete}
|
||||||
|
\label{fig:grafana}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Kibana}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Kibana\cite{kibana} jelentősen hasonlít a Grafanához, azonban amíg a utóbbit inkább az időben változó metrikák vizualizálására használják például processzor leterheltség vagy memória használat,
|
||||||
|
addig az előbbit elsődlegesen az Elasticsearch\footenotemark adatok, főként napló bejegyzések, analizálására használják.
|
||||||
|
|
||||||
|
\footnotetext{Ingyenes és nyílt forráskódú index alapú keresőmotor}
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/kibana-dashboard.png}
|
||||||
|
\caption{Egy példa a Kibana kezelőfelületére}
|
||||||
|
\label{fig:kibana}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Kubernetes Dashboard (Web UI)}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Kubernetes Dashboard\cite{kubernetes-dashboard} elsősorban nem a különböző adatok vizualizálását szolgálja, inkább a klaszter menedzselését próbálja egyszerűbbé és jobban áttekinthetővé tenni.
|
||||||
|
Azonban egy jó példa arra, hogy egy rendszer webes kezelőfelületének, milyennek is kell lennie.
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/kubernetes-dashboard.png}
|
||||||
|
\caption{A Kubernetes Dashboard felülete}
|
||||||
|
\label{fig:kibana}
|
||||||
|
\end{figure}
|
0
docs/thesis/content/birdmap-kubernetes.tex
Normal file
79
docs/thesis/content/birdmap-technologies.aux
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\citation{git}
|
||||||
|
\citation{trello}
|
||||||
|
\citation{vs}
|
||||||
|
\citation{vs-code}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {chapter}{\numberline {4}Használt technológiák}{10}{chapter.4}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\@writefile{lot}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\newlabel{chapt:birdmap-technologies}{{4}{10}{Kubernetes Dashboard (Web UI)}{chapter.4}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {4.1}A fejlesztési folyamat technológiái}{10}{section.4.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.1.1}Git}{10}{subsection.4.1.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.1.2}Trello}{10}{subsection.4.1.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.1.3}Visual Studio}{10}{subsection.4.1.3}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.1.4}Visual Studio Code}{10}{subsection.4.1.4}\protected@file@percent }
|
||||||
|
\citation{jwt}
|
||||||
|
\@writefile{lof}{\select@language{magyar} \contentsline {figure}{\numberline {4.1.}{\ignorespaces Egy példa állapot a Trello felületére a fejlesztés során\relax }}{11}{figure.caption.8}\protected@file@percent }
|
||||||
|
\newlabel{fig:trello}{{4.1}{11}{Egy példa állapot a Trello felületére a fejlesztés során\relax }{figure.caption.8}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {4.2}Backend technológiák}{11}{section.4.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.2.1}ASP.NET Core}{11}{subsection.4.2.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.2.2}Entity Framework Core}{11}{subsection.4.2.2}\protected@file@percent }
|
||||||
|
\citation{nlog}
|
||||||
|
\citation{react}
|
||||||
|
\citation{material}
|
||||||
|
\citation{material-ui}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.2.3}JSON Web Token}{12}{subsection.4.2.3}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.2.4}SignalR}{12}{subsection.4.2.4}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.2.5}MQTT.NET}{12}{subsection.4.2.5}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.2.6}NLog}{12}{subsection.4.2.6}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {4.3}Frontend technológiák}{12}{section.4.3}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.3.1}React.js}{12}{subsection.4.3.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.3.2}Material UI}{12}{subsection.4.3.2}\protected@file@percent }
|
||||||
|
\citation{apexcharts}
|
||||||
|
\citation{google-map-react}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.3.3}Apexcharts}{13}{subsection.4.3.3}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {4.3.4}Google Maps Api}{13}{subsection.4.3.4}\protected@file@percent }
|
||||||
|
\@setckpt{content/birdmap-technologies}{
|
||||||
|
\setcounter{page}{14}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{0}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{4}
|
||||||
|
\setcounter{section}{3}
|
||||||
|
\setcounter{subsection}{4}
|
||||||
|
\setcounter{subsubsection}{0}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{1}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{2}
|
||||||
|
\setcounter{bookmark@seq@number}{45}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{4}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{2}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
\setcounter{romanPage}{3}
|
||||||
|
}
|
134
docs/thesis/content/birdmap-technologies.tex
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter{Használt technológiák}
|
||||||
|
\label{chapt:birdmap-technologies}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ezzel a fejezettel az a célom, hogy ismertessem a fejlesztés során, illetve az alkalmazásom által használt technológiákat,
|
||||||
|
hogy a következő fejezetekben alapozni tudjak ezeknek az ismeretére.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{A fejlesztési folyamat technológiái}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a szakaszban azokat az eszközöket, alkalmazásokat és fejlesztőkörnyezeteket mutatom be, melyeket a fejlesztés során, a fejlesztéshez használtam.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Git}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Git\cite{git} egy verziókezelő rendszer. Használatával a felhasználó le tudja menteni egy adott fájlrendszerben található fájlok állapotát.
|
||||||
|
Megkönnyíti az egy projekten dolgozó programozók közötti kooperációt. Manapság lassan elképzelhetetlen a fejlesztés valamilyen verziókezelő használata nélkül.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Trello}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Trello\cite{trello} egy webes lista készítő és kezelő alkalmazás.
|
||||||
|
Azért használtam a fejlesztés során, mert szerettem volna egy helyet, ami tükrözi a fejlesztés állapotát, ahova le tudom írni az alkalmazással kapcsolatos ötleteimet.
|
||||||
|
Különböző listákban tároltam a fejlesztésre váró és a kész feladatokat szerver, kliens és egyéb szerint.
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/trello-cropped.png}
|
||||||
|
\caption{Egy példa állapot a Trello felületére a fejlesztés során}
|
||||||
|
\label{fig:trello}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Visual Studio}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Visual Studio\cite{vs} a Microsoft fejlesztőkörnyezete. Jól alkalmazható a .NET keretrendszer technológiáival, ezért ezt használtam a szerveroldal fejlesztése során.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Visual Studio Code}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Egy másik Microsoft termék, viszont a fentivel ellentétben a Visual Studio Code\cite{vs-code} inkább szövegszerkeztő, mint fejlesztőkörnyezet.
|
||||||
|
Ennek köszönhetően jelentősen gyorsabb és egyszerűbb a használata. Különféle bővítmények használatával nagyon jó program nyelv támogatottságot lehet elérni.
|
||||||
|
Többek között ezen okok miatt preferáltam a kliensoldal fejlesztésére.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Backend technológiák}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a szakaszban a szerveroldal megvalósítására használt .NET technológiákat mutatom be. A választásom több ok miatt esett a .NET keretrendszer használatára.
|
||||||
|
|
||||||
|
Egyrészt úgy gondoltam, hogy az alkalmazásom fajsúlyosabb részét inkább a kliensoldal fogja képezni ezért, hogy arra több energiát tudjak fordítani, valami olyat választottam,
|
||||||
|
amivel már foglalkoztam korábban, amivel gyorsabban és rutinosabban megy a fejlesztés.
|
||||||
|
|
||||||
|
Másrészt nemrég jelent meg a .NET új 5-ös verziója, melynek használatával jelentős teljesítmény javulást ígértek több területen is, és úgy gondoltam, hogy ez a projekt tökéletes lenne
|
||||||
|
ennek próbatételére.
|
||||||
|
|
||||||
|
Mindemellett a .NET teljesen platformüggetlen, mely az egyik legfontosabb követelmény volt az alkalmazással szemben.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{ASP.NET Core}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az ASP.NET Core a .NET család ingyenes, nyílt forráskódú webes keretrendszere. Gyors és moduláris fejlesztést tesz lehetővé, mely főként a NuGet csomagoknak köszönhető.
|
||||||
|
Használatána egyik előnye, hogy ugyan az a C\# kód tud futni a szerver éa a kliens oldalon, de támogat más kliens oldali keretrendszereket is, mint például az Angular-t, a Vue.js-t
|
||||||
|
vagy a React.js-t.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Entity Framework Core}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az Entity Framework Core (röviden EF Core) egy objektum-relációs leképező keretrendszer a .NET-hez. Az adatbázissal való kommunikációt könnyítését szolgálja.
|
||||||
|
Használatával C\#-ban lehet adatbázis lekérdezéseket írni a LINQ (Language-Integrated Query) segítségével.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{JSON Web Token}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az autorizációt többféleképpen meg lehet oldani egy alkalmazás szempontjából. Az egyik ilyen megoldás a JSON Web Token-ek (röviden JWT) \cite{jwt} használata,
|
||||||
|
ami nem más mint egy szabvány, mely módszert ad a felek közötti információ biztonságos továbbítására JSON objektumokkal.
|
||||||
|
Ezen objektumok adatokat tárolhatnak a felhasználóról például a neve vagy a szerepe, melyek segítségével a szerver eldöntheti,
|
||||||
|
hogy van-e jogosultsága hívni az adott végpontot.
|
||||||
|
|
||||||
|
A Microsoft-nak van egy beépített szoftvercsomagja, mellyel ilyen tokeneket lehet készíteni és validálni.
|
||||||
|
A szerveroldal jogosultság kezelését ezzel a csomaggal oldottam meg.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{SignalR}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A SignalR egy .NET szoftvercsomag, mely lehetővé teszi a szerveroldal számára a kliensekkel való aszinkron kommunikációt.
|
||||||
|
A szerver valós időben tud értesítéseket küldeni a kliensek számára, amelyek feliratkoztak az ilyen eseményekre.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{MQTT.NET}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az MQTT.NET is egy .NET szoftvercsomag, mely a Birdnetes által is használt, a \ref{subsect:mqtt}-es alfejezetben bemutatott MQTT kommunikáció C\# nyelvű megvalósítását szolgálja.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{NLog}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A szerveroldali naplózás megvalósítására több szoftvercsomag is létezik. Az NLog\cite{nlog}-ot választottam, egyrészt mert egyszerű a használata,
|
||||||
|
másrészt mert már használtam korábban.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Frontend technológiák}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a szakaszban a kliensoldalon használt technológiákat mutatom be.
|
||||||
|
Választásomnál fő motiváció az volt, hogy szerettem volna valami újat kipróbálni, aminek nincs köze a .NET keretrendszerhez.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{React.js}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A React.js\cite{react} egy JavaScript szoftvercsomag, melyet webes felületek fejlesztésére használnak.
|
||||||
|
Fő építő elemei a komponensek, melyek elszeparált újrafelhasználható felület egységek.
|
||||||
|
Használatának egyik előnye, hogy automatizált az állapot kezelés, tehát ha változik egy komponens állapota, akkor a React újra-rendereli azt.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Material UI}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Material\cite{material} elsősorban egy kezelőfelület tervezési útmutató a Google által, melyet követve szép és minőségi felületeket lehet készíteni.
|
||||||
|
|
||||||
|
A Material UI\cite{material-ui} egy szoftvercsomag, mely ezeket az útmutatásokat követő egyszerű React komponenseket tartalmaz.
|
||||||
|
Alkalmazásával könnyő esztétikus felhasználói felületeket készíteni, minimalizált a CSS használattal.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Apexcharts}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az Apexcharts\cite{apexcharts} egy nyílt forráskódú JavaScript szoftvercsomag, amellyel könnyen konfigurálható, modern kinézetű diagrammokat lehet készíteni.
|
||||||
|
Sokféle kliensoldali (és szerveroldali) technológiát támogat, köztük a React-et is. A kezelőfelületen található vizualizációk szinte összes elemét ennek használatával csináltam.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Google Maps Api}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Google szinte összes termékének van API-ja, ami lehetővé teszi a programozók számára, hogy integrálják ezeket saját alkalmazásaikban.
|
||||||
|
A Google Maps sincs másképp és mivel ennek interfésze külön támogatja a hőtérképes réteg használatát is, nem gondoltam, hogy ettől jobb eszközt tudnék találni a feladat megvalósítására.
|
||||||
|
|
||||||
|
A Google Maps API-t, ami alapvetően csak egy JavaScript csomag, rengetegen újracsomagolják, hogy különböző részét, különböző keretrendszerekben is lehessen használni.
|
||||||
|
Ezek közül én a Google Map React\cite{google-map-react}-et választottam, egyrészt mert támogatja a hőtérképes réteg használatát,
|
||||||
|
másrészt mert lehetővé teszi a térképen való React komponensek renderelését az alapértelmezett markerek helyett.
|
0
docs/thesis/content/birdmap-test.tex
Normal file
72
docs/thesis/content/birdnetes-introduction.aux
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\citation{birdnetes-tdk}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {chapter}{\numberline {2}A Birdnetes bemutatása}{3}{chapter.2}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\@writefile{lot}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\newlabel{chapt:birdnetes-introduction}{{2}{3}{A szakdolgozat felépítése}{chapter.2}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {2.1}Gyors elméleti összefoglaló}{3}{section.2.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {2.1.1}Cloud, felhő}{3}{subsection.2.1.1}\protected@file@percent }
|
||||||
|
\citation{docker}
|
||||||
|
\citation{kubernetes}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.1.1.1}Mikroszolgáltatások}{4}{subsubsection.2.1.1.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.1.1.2}Konténerek}{4}{subsubsection.2.1.1.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.1.1.3}Kubernetes}{4}{subsubsection.2.1.1.3}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {2.1.2}MQTT}{4}{subsection.2.1.2}\protected@file@percent }
|
||||||
|
\newlabel{subsect:mqtt}{{2.1.2}{4}{MQTT}{subsection.2.1.2}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {2.1.3}Open API}{4}{subsection.2.1.3}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {2.2}Rendszerszintű architektúra}{5}{section.2.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsection}{\numberline {2.2.1}Főbb komponensek}{5}{subsection.2.2.1}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \contentsline {figure}{\numberline {2.1.}{\ignorespaces A Birdnetes rendszer architektúrája\relax }}{5}{figure.caption.4}\protected@file@percent }
|
||||||
|
\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
|
||||||
|
\newlabel{fig:birdnetes-components}{{2.1}{5}{A Birdnetes rendszer architektúrája\relax }{figure.caption.4}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.2.1.1}IoT eszközök}{5}{subsubsection.2.2.1.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.2.1.2}Input Service}{6}{subsubsection.2.2.1.2}\protected@file@percent }
|
||||||
|
\newlabel{subsect:birdnetes-input-service}{{2.2.1.2}{6}{Input Service}{subsubsection.2.2.1.2}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.2.1.3}AI Service}{6}{subsubsection.2.2.1.3}\protected@file@percent }
|
||||||
|
\newlabel{subsect:birdnetes-ai-service}{{2.2.1.3}{6}{AI Service}{subsubsection.2.2.1.3}{}}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.2.1.4}Guard Service}{6}{subsubsection.2.2.1.4}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {subsubsection}{\numberline {2.2.1.5}Command and Control Service}{6}{subsubsection.2.2.1.5}\protected@file@percent }
|
||||||
|
\@setckpt{content/birdnetes-introduction}{
|
||||||
|
\setcounter{page}{7}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{1}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{2}
|
||||||
|
\setcounter{section}{2}
|
||||||
|
\setcounter{subsection}{1}
|
||||||
|
\setcounter{subsubsection}{5}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{1}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{1}
|
||||||
|
\setcounter{bookmark@seq@number}{21}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{3}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{3}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
\setcounter{romanPage}{3}
|
||||||
|
}
|
134
docs/thesis/content/birdnetes-introduction.tex
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter{A Birdnetes bemutatása}
|
||||||
|
\label{chapt:birdnetes-introduction}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ebben a fejezetben ismertetem a Birdnetes mikroszolgáltatás rendszerének architektúráját és az általa használt technológiákat.
|
||||||
|
Részletesen kifejtem az alkalmazásom szempontjából fontos komponensek feladatát és működését.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Gyors elméleti összefoglaló}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Ez a szakasz nem azt a célt szolgálja, hogy minnél részletesebb képet mutasson az itt leírt technológiákról.
|
||||||
|
Arra sokkal jobb eszköz Pünkösdi Marcellnek és Torma Kristófnak, a Birdnetes alkótóinak TDK dolgozata\cite{birdnetes-tdk}.
|
||||||
|
Ez csupán egy rövid összefoglaló a Birdnetes működésének megértése szempontjából elengedhetetlen technológiákról és elvekről,
|
||||||
|
hogy valamennyire érthetőbbek legyenek a fejezetben elhangzó kifejezések.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Cloud, felhő}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A cloud lényegében annyit jelent, hogy a szervert, amin az alkalmazás fut, nem a fejlesztőnek kell üzemeltetnie,
|
||||||
|
hanem valamilyen másik szervezet\footnotemark által vannak karban tartva.
|
||||||
|
Ez több okból is hasznos:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Olcsóbb}. Nem kell berendezéseket vásárolni, nincs üzemeltetési díj. Az egyetlen költség a bérlés, ami általában töredéke annak, amit akkor fizetnénk ha magunk csinálnánk az egészet.
|
||||||
|
\item \textbf{Gyorsabb fejlesztés}. Az alkalmazás futtatására használt szervereket általában a fejlesztő nem látja, ezekkel nem kell foglalkoznia. Ha az alkalmazásnak hirtelen nagyobb erőforrás igénye lesz, a rendszer automatikusan skálázódik.
|
||||||
|
\item \textbf{Nagyobb megbízhatóság}. Az ilyen szolgáltatást nyújtó szervezeteknek ez az egyik legnagyobb feladata. Az alkalmazás bárhol és bármikor elérhető.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\footnotetext{Ilyenek például a Microsoft Azure, az Amazon Web Services vagy a Google Cloud.}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{Mikroszolgáltatások}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A mikroszolgáltatások nem sok mindenben különböznek egy általános szolgáltatástól.
|
||||||
|
Ugyan úgy valamilyen kéréseket kiszolgáló egységek, legyen az web kérések kiszolgálása HTTP-n keresztül
|
||||||
|
vagy akár parancssori utasítások feldolgozása. Az egyetlen fő különbség az a szolgáltatások felelősségköre.
|
||||||
|
A mikroszolgáltatások fejlesztésénél a fejlesztők elsősorban arra törekednek, hogy egy komponensnek minnél kevesebb feladata és függősége legyen,
|
||||||
|
ezzel megnő a tesztelhetőség és könyebb a skálázhatóság.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{Konténerek}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A konténer technikailag semmivel sem több mint egy Linux-on futó processz amelyre különböző korlátozásokat szabtak.
|
||||||
|
Ilyen korlátozások lehetnek például, hogy a konténer nem látja a teljes fájlrendszert, annak csak egy kijelölt részét,
|
||||||
|
megadható a konténer által használható processzor és memória igény vagy akár korlátozható az is, hogy a konténer hogyan használhatja a hálózatot.
|
||||||
|
Léteznek eszközök, például a Docker\cite{docker}, mely lehetővé teszi a fejlesztők számára az ilyen konténerek könnyed létrehozását és futtatását.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{Kubernetes}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Kubernetes\cite{kubernetes} az ilyen komplex konténerizált mikroszolgáltatás rendszerek menedzselésének könnyítését szolgálja.
|
||||||
|
Kihasználja és ötvözi az imént említett technológiák előnyeit, hogy egy robosztus rendszert alkosson.
|
||||||
|
Használatával felgyorsulhat és automatizált lehet az egyes konténerek telepítése, futtatása, de talán a legfőbb előnye,
|
||||||
|
hogy segítségével könnyedén megoldható a rendszert ért terhelési igények szerinti dinamikus skálázódás.
|
||||||
|
Azok a mikroszolgáltatások, amikre a rendszernek épp nincs szüksége, nem futnak, nem igényelnek erőforrást a szerveren,
|
||||||
|
így nem kell utánnuk fizetni sem. Ezzel ellentétben, ha valamely szolgáltatás után hirtelen megnő az igény,
|
||||||
|
akkor az könnyedén duplikálható.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{MQTT}
|
||||||
|
\label{subsect:mqtt}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az MQTT (Message Queue Telemetry Transport) az egy kliens-szerver publish/subscribe üzenetküldő protokoll. Könnyű implementálni és alacsony a sávszélesség igénye,
|
||||||
|
mellyel tökéletes jelöltje a Machine to Machine (M2M), illetve az Internet of Things (IoT) kommunikáció megvalósítására.
|
||||||
|
Működéséhez szükség van egy szerverre, amelynek feladata a beérkező üzenetek továbbküldése témák alapján. Egyes kliensek fel tudnak iratkozni bizonyos témákra, míg más kliensek publikálnak
|
||||||
|
és a szerver levezényli a két fél között a kommunikációt.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Open API}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az Open API egy nyilvános alkalmazás-programozási leíró, amely a fejlesztők számára hozzáférést biztosít egy másik alkalmazáshoz.
|
||||||
|
Az API-k lírják és meghatározzák, hogy egy alkalmazás hogyan kommunikálhat egy másikkal,
|
||||||
|
melyet használva a fejlesztők könnyedén képesek a kommunikációra képes kódot írni vagy generálni.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{Rendszerszintű architektúra}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Birdnetes fejlesztése során kifejezetten fontos szerepe volt a mikroszolgáltatás alapú rendszerek elvei követésének.
|
||||||
|
A rendszer egy Kubernetes klaszterben van telepítve és több kisebb komponensből áll, melyek egymás között a HTTP és az MQTT protokollok segítségével kommunikálnak.
|
||||||
|
A rendszer összes szolgáltatásának van egy Open API leírója, melyet használva hamar volt egy olyan kódbázisom, amely képes volt a rendszerrel való kommunikációra.
|
||||||
|
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsection{Főbb komponensek}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A \ref{fig:birdnetes-components}-es ábrán láthatóak a rendszer komponensei, melyek mindegyike egy-egy mikroszolgáltatás.
|
||||||
|
Az egymás mellett lévő kék levélborítékok az MQTT kommunikációt jelölik,
|
||||||
|
amellyel például a természetben elhelyezett eszközök felé irányuló kommunikáció is történik.
|
||||||
|
A következő alszakaszokban bemutatom az alkalmazásom szempontjából fontosabb komponenseket.
|
||||||
|
|
||||||
|
\begin{figure}[!ht]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=150mm, keepaspectratio]{figures/architecture-redesigned.png}
|
||||||
|
\caption{A Birdnetes rendszer architektúrája}
|
||||||
|
\label{fig:birdnetes-components}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{IoT eszközök}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Szőlőültetvényekben telepített eszközök, melyek adott időközönként publikálják állapotaikat egyéb metaadatokkal egy üzenetsoron.
|
||||||
|
Emellett folyamatosan hangfelvételt készítenek a beépített mikrofonjaikkal, mely hangfelvételekről egy másik belső szenzor eldönti,
|
||||||
|
hogy érdemes-e felküldeni a rendszerbe, ha igen, akkor egy másik üzenetsoron publikálják ezeket a hangfelvételeket.
|
||||||
|
Tartalmaznak még egy hangszórót is, mely a madarak elijesztését szolgálja.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{Input Service}
|
||||||
|
\label{subsect:birdnetes-input-service}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A kihelyezett IoT eszközök által felvett hangfájlok ezen a komponensen keresztül érkeznek be a rendszerbe.
|
||||||
|
Itt történik a hanganyaghoz tartozó metaadatok lementése az Input Service saját adatbázisába.
|
||||||
|
Ilyenek például a beküldő eszköz azonosítója, a beérkezés dátuma vagy a hangüzenet rendszerszintű egyedi azonosítója.
|
||||||
|
Amint a szolgáltatás a berékezett üzenettel kapcsolatban elvégezte az összes feladatát,
|
||||||
|
publikál egy üzenetet az MQTT üzenetsorra a többi kliensnek feldolgozásra.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{AI Service}
|
||||||
|
\label{subsect:birdnetes-ai-service}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Az AI Service példányai fogadják az Input Service-től érkező üzeneteket és elkezdik klasszifikálni az abban található hanganyagot.
|
||||||
|
Meghatározzák, hogy a hanganyag mekkora valószínűséggel volt seregély hang vagy sem.
|
||||||
|
Ennek eredményét a hangminta egyedi azonosítójával együtt publikálják egy másik üzenetsoron.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{Guard Service}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Guard Service feliratkozik az AI Service által publikált üzenetek témájára
|
||||||
|
és valamilyen valószínűségi kritérium alapján eldönti, hogy a hangminta tartalmaz-e seregély hangot.
|
||||||
|
Ha igen, akkor az üzenetsoron küld egy riasztás parancsot a hanganyagot küldő eszköznek.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\subsubsection{Command and Control Service}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A Command and Control Service az előzőekkel ellentétben egyáltalán nem vesz részt a minták fogadásában, feldolgozásában vagy kezelésében.
|
||||||
|
Felelősége az eszközök és azok szenzorai állapotának menedzselése és követése.
|
||||||
|
Ezen keresztül lehet az egyes eszközöket ki- és bekapcsolni.
|
51
docs/thesis/content/introduction.aux
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {chapter}{\numberline {1}Bevezetés}{1}{chapter.1}\protected@file@percent }
|
||||||
|
\@writefile{lof}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\@writefile{lot}{\select@language{magyar} \addvspace {10\p@ }}
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {1.1}A probléma}{1}{section.1.1}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {1.2}A megoldás}{1}{section.1.2}\protected@file@percent }
|
||||||
|
\@writefile{toc}{\select@language{magyar} \contentsline {section}{\numberline {1.3}A szakdolgozat felépítése}{2}{section.1.3}\protected@file@percent }
|
||||||
|
\@setckpt{content/introduction}{
|
||||||
|
\setcounter{page}{3}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{0}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{1}
|
||||||
|
\setcounter{section}{3}
|
||||||
|
\setcounter{subsection}{0}
|
||||||
|
\setcounter{subsubsection}{0}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{0}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{0}
|
||||||
|
\setcounter{bookmark@seq@number}{6}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{2}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{1}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
\setcounter{romanPage}{3}
|
||||||
|
}
|
38
docs/thesis/content/introduction.tex
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\chapter{\bevezetes}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
Szőlőtulajdonosoknak éves szinten jelentős kárt okoznak a seregélyek, akik előszeretettel választják táplálékul a megtermelt szőlőt.
|
||||||
|
Erre a problémára dolgoztak ki a tanszéken diáktársaim egy felhő alapú konténerizált rendszert, a Birdnetes-t
|
||||||
|
mely a természetben elkelyezett eszközökkel kommunikál, azokat vezérli.
|
||||||
|
Az eszközök bizonyos időközönként hangfelvételt készítenek a környezetükről,
|
||||||
|
majd valamilyen formában elküldik ezeket a felvételeket a központi rendszernek,
|
||||||
|
amely egy erre a célra kifejlesztett mesterséges intelligenciát használva eldönti
|
||||||
|
a felvételről, hogy azon található-e seregély hang vagy sem.
|
||||||
|
Ha igen akkor jelez a felvételt küldő eszköznek, hogy szólaltassa meg a riasztó
|
||||||
|
berendezését, hogy elijessze a madarakat.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{A probléma}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A jelen rendszer használata során nincs vizuális visszacsatolás az esetleges riasztásokról azok gyakoriságáról
|
||||||
|
és a rendszer állapotáról sem. Különböző diagnosztikai eszközök ugyan implementálva lettek mint például
|
||||||
|
a logolás vagy a hiba bejelentés, de ezek használata nehézkes, nem kézenfekvő.
|
||||||
|
Szükség van valamire amivel egy helyen és egyszerűen lehet kezelni és felügyelni a rendszer egyes elemeit.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{A megoldás}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A jelen szakdolgozat egy olyan webes alkalmazás elkészítését dokumentálja, melyel a felhasználók képesek
|
||||||
|
a természetben elhelyezett eszközök állapotát vizsgálni, azokat akár ki és bekapcsolni igény szerint.
|
||||||
|
Az egyes rendszer eseményeket vizsgálva a szoftver statisztikákat készít, melyeket különböző diagrammokon ábrázolok.
|
||||||
|
Ilyen statisztikák például, hogy időben melyik eszköz mikor észlelt madár hangot, vagy hogy hány hang üzenet érkezik
|
||||||
|
az eszközöktől másodpercenként.
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
\section{A szakdolgozat felépítése}
|
||||||
|
%----------------------------------------------------------------------------
|
||||||
|
A szakdolgozatom első részében, a \ref{chapt:birdnetes-introduction}. fejezetben, bemutatom a Birdnetes felépítését, az egyes komponensek közötti kapcsolatokat és a technológiát, amire épült.
|
||||||
|
A 3. fejezetben ismertetem a jelenleg az iparban is használt mikroszolgáltatás működését vizualizáló alternatívákat, majd a saját megoldásom tervezetét, az arra vonatkozó elvárásokat.
|
||||||
|
A 4. fejezetben az alkalmazásom által használt technológiákat mutatom be, ezzel előkészítve az 5. és 6. fejezetet, ahol ismertetem a szerver- és kliensalkalmazások felépítését.
|
||||||
|
A 7. és 8. fejezet az alkalmazás teszteléséről és telepítéséről szól.
|
||||||
|
Az utolsó fejezetben értékelem a munkám eredményét, levonom a tapasztalatokat és bemutatok néhány továbbfejlesztési lehetőséget.
|
0
docs/thesis/content/summary.tex
Normal file
BIN
docs/thesis/figures/TeXstudio.png
Normal file
After Width: | Height: | Size: 168 KiB |
BIN
docs/thesis/figures/architecture-redesigned.png
Normal file
After Width: | Height: | Size: 43 KiB |
3
docs/thesis/figures/architecture-redesigned.svg
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
docs/thesis/figures/bme_logo.pdf
Normal file
7
docs/thesis/figures/convert.cmd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@echo off
|
||||||
|
for %%j in (*.eps) do (
|
||||||
|
echo converting file "%%j"
|
||||||
|
epstopdf "%%j"
|
||||||
|
)
|
||||||
|
echo done .
|
||||||
|
|
BIN
docs/thesis/figures/grafana.png
Normal file
After Width: | Height: | Size: 212 KiB |
BIN
docs/thesis/figures/kibana-dashboard.png
Normal file
After Width: | Height: | Size: 375 KiB |
BIN
docs/thesis/figures/kubernetes-dashboard.png
Normal file
After Width: | Height: | Size: 196 KiB |
BIN
docs/thesis/figures/mqtt-communication-sequence.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
docs/thesis/figures/swagger-ui.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
docs/thesis/figures/trello-cropped.png
Normal file
After Width: | Height: | Size: 56 KiB |
1910
docs/thesis/huplain.bst
Normal file
67
docs/thesis/ideas.txt
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
Forma
|
||||||
|
|
||||||
|
- Kivonat
|
||||||
|
- Abstract
|
||||||
|
- Bevezetés
|
||||||
|
- Az alaphelyzet
|
||||||
|
- A probléma
|
||||||
|
- A megoldás
|
||||||
|
- Diplomaterv felépítésének bemutatása
|
||||||
|
- Az alaphelyzet részletes bemutatása
|
||||||
|
- mikroszolgáltatás alapú rendszerek alapelvei
|
||||||
|
- a tanszéken fejlesztett elosztott madárhang azonosító megoldás
|
||||||
|
teljeskörű bemutatása
|
||||||
|
- A megoldás tervezete
|
||||||
|
- mikroszolgáltatások működését vizualizáló alternatívák
|
||||||
|
- használati tervek
|
||||||
|
- design terverk
|
||||||
|
- A használt technológiák
|
||||||
|
- fejlesztési folyamat
|
||||||
|
- fejlesztő környezet(ek)
|
||||||
|
- agilis módszertan
|
||||||
|
- trello
|
||||||
|
- verziókezelés git
|
||||||
|
- Backend
|
||||||
|
- ASP.NET Core 5
|
||||||
|
- SignalR
|
||||||
|
- EFCore 5
|
||||||
|
- Frontend
|
||||||
|
- material-ui
|
||||||
|
- React.js
|
||||||
|
- Apexcharts
|
||||||
|
- google-map-react
|
||||||
|
- Tesztkörnyezet
|
||||||
|
- WinForms .NET 5 (githubról)
|
||||||
|
- Kubenetes
|
||||||
|
- Backend
|
||||||
|
- Felépítés architektúra
|
||||||
|
- ábra
|
||||||
|
- konfigurálás
|
||||||
|
- startup mindenhol
|
||||||
|
- ...
|
||||||
|
- DAL
|
||||||
|
- EFCore
|
||||||
|
- Entitások
|
||||||
|
- Seed
|
||||||
|
- ...
|
||||||
|
- BLL
|
||||||
|
- servicek
|
||||||
|
- API
|
||||||
|
- controllerek
|
||||||
|
- servicek
|
||||||
|
- mqtt.net
|
||||||
|
- signalR
|
||||||
|
- Frontend
|
||||||
|
- felépítés architektúra
|
||||||
|
- App and navigation
|
||||||
|
- Components and services
|
||||||
|
- kép minden felületről
|
||||||
|
- kép user és admin felhasználók különbségéről
|
||||||
|
- Device service
|
||||||
|
- dashboard systeminfo service
|
||||||
|
- kép a skeletonról -> igazi adat
|
||||||
|
- Tesztkörnyezet
|
||||||
|
- Kubenetes
|
||||||
|
- Összefoglaló
|
||||||
|
- Értékelés, eredmények, tapasztalatok.
|
||||||
|
- Továbbfejlesztési lehetőségek
|
44
docs/thesis/include/declaration.aux
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\@setckpt{include/declaration}{
|
||||||
|
\setcounter{page}{2}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{0}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{0}
|
||||||
|
\setcounter{section}{0}
|
||||||
|
\setcounter{subsection}{0}
|
||||||
|
\setcounter{subsubsection}{0}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{0}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{0}
|
||||||
|
\setcounter{bookmark@seq@number}{0}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{2}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{0}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
}
|
32
docs/thesis/include/declaration.tex
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
\selectlanguage{magyar}
|
||||||
|
\pagenumbering{gobble}
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Nyilatkozat
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\begin{center}
|
||||||
|
\large
|
||||||
|
\textbf{HALLGATÓI NYILATKOZAT}\\
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
Alulírott \emph{\vikszerzoVezeteknev{} \vikszerzoKeresztnev}, szigorló hallgató kijelentem, hogy ezt a \vikmunkatipusat{} meg nem engedett segítség nélkül, saját magam készítettem, csak a megadott forrásokat (szakirodalom, eszközök stb.) használtam fel. Minden olyan részt, melyet szó szerint, vagy azonos értelemben, de átfogalmazva más forrásból átvettem, egyértelműen, a forrás megadásával megjelöltem.
|
||||||
|
|
||||||
|
Hozzájárulok, hogy a jelen munkám alapadatait (szerző(k), cím, angol és magyar nyelvű tartalmi kivonat, készítés éve, konzulens(ek) neve) a BME VIK nyilvánosan hozzáférhető elektronikus formában, a munka teljes szövegét pedig az egyetem belső hálózatán keresztül (vagy autentikált felhasználók számára) közzétegye. Kijelentem, hogy a benyújtott munka és annak elektronikus verziója megegyezik. Dékáni engedéllyel titkosított diplomatervek esetén a dolgozat szövege csak 3 év eltelte után válik hozzáférhetővé.
|
||||||
|
|
||||||
|
\begin{flushleft}
|
||||||
|
\vspace*{1cm}
|
||||||
|
Budapest, \today
|
||||||
|
\end{flushleft}
|
||||||
|
|
||||||
|
\begin{flushright}
|
||||||
|
\vspace*{1cm}
|
||||||
|
\makebox[7cm]{\rule{6cm}{.4pt}}\\
|
||||||
|
\makebox[7cm]{\emph{\vikszerzoVezeteknev{} \vikszerzoKeresztnev}}\\
|
||||||
|
\makebox[7cm]{hallgató}
|
||||||
|
\end{flushright}
|
||||||
|
\thispagestyle{empty}
|
||||||
|
|
||||||
|
\vfill
|
||||||
|
\clearpage
|
||||||
|
\thispagestyle{empty} % an empty page
|
||||||
|
|
||||||
|
\selectthesislanguage
|
54
docs/thesis/include/guideline.tex
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
\selecthungarian
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Rovid formai es tartalmi tajekoztato
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
\footnotesize
|
||||||
|
\begin{center}
|
||||||
|
\large
|
||||||
|
\textbf{\Large Általános információk, a diplomaterv szerkezete}\\
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
A diplomaterv szerkezete a BME Villamosmérnöki és Informatikai Karán:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Diplomaterv feladatkiírás
|
||||||
|
\item Címoldal
|
||||||
|
\item Tartalomjegyzék
|
||||||
|
\item A diplomatervező nyilatkozata az önálló munkáról és az elektronikus adatok kezeléséről
|
||||||
|
\item Tartalmi összefoglaló magyarul és angolul
|
||||||
|
\item Bevezetés: a feladat értelmezése, a tervezés célja, a feladat indokoltsága, a diplomaterv felépítésének rövid összefoglalása
|
||||||
|
\item A feladatkiírás pontosítása és részletes elemzése
|
||||||
|
\item Előzmények (irodalomkutatás, hasonló alkotások), az ezekből levonható következtetések
|
||||||
|
\item A tervezés részletes leírása, a döntési lehetőségek értékelése és a választott megoldások indoklása
|
||||||
|
\item A megtervezett műszaki alkotás értékelése, kritikai elemzése, továbbfejlesztési lehetőségek
|
||||||
|
\item Esetleges köszönetnyilvánítások
|
||||||
|
\item Részletes és pontos irodalomjegyzék
|
||||||
|
\item Függelék(ek)
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
Felhasználható a következő oldaltól kezdődő \LaTeX diplomatervsablon dokumentum tartalma.
|
||||||
|
|
||||||
|
A diplomaterv szabványos méretű A4-es lapokra kerüljön. Az oldalak tükörmargóval készüljenek (mindenhol 2,5~cm, baloldalon 1~cm-es kötéssel). Az alapértelmezett betűkészlet a 12 pontos Times New Roman, másfeles sorközzel, de ettől kismértékben el lehet térni, ill. más betűtípus használata is megengedett.
|
||||||
|
|
||||||
|
Minden oldalon -- az első négy szerkezeti elem kivételével -- szerepelnie kell az oldalszámnak.
|
||||||
|
|
||||||
|
A fejezeteket decimális beosztással kell ellátni. Az ábrákat a megfelelő helyre be kell illeszteni, fejezetenként decimális számmal és kifejező címmel kell ellátni. A fejezeteket decimális aláosztással számozzuk, maximálisan 3 aláosztás mélységben (pl. 2.3.4.1.). Az ábrákat, táblázatokat és képleteket célszerű fejezetenként külön számozni (pl. 2.4. ábra, 4.2. táblázat vagy képletnél (3.2)). A fejezetcímeket igazítsuk balra, a normál szövegnél viszont használjunk sorkiegyenlítést. Az ábrákat, táblázatokat és a hozzájuk tartozó címet igazítsuk középre. A cím a jelölt rész alatt helyezkedjen el.
|
||||||
|
|
||||||
|
A képeket lehetőleg rajzoló programmal készítsék el, az egyenleteket egyenlet-szerkesztő segítségével írják le (A \LaTeX~ehhez kézenfekvő megoldásokat nyújt).
|
||||||
|
|
||||||
|
Az irodalomjegyzék szövegközi hivatkozása történhet sorszámozva (ez a preferált megoldás) vagy a Harvard-rendszerben (a szerző és az évszám megadásával). A teljes lista névsor szerinti sorrendben a szöveg végén szerepeljen (sorszámozott irodalmi hivatkozások esetén hivatkozási sorrendben). A szakirodalmi források címeit azonban mindig az eredeti nyelven kell megadni, esetleg zárójelben a fordítással. A listában szereplő valamennyi publikációra hivatkozni kell a szövegben (a \LaTeX-sablon a Bib\TeX~segítségével mindezt automatikusan kezeli). Minden publikáció a szerzők után a következő adatok szerepelnek: folyóirat cikkeknél a pontos cím, a folyóirat címe, évfolyam, szám, oldalszám tól-ig. A folyóiratok címét csak akkor rövidítsük, ha azok nagyon közismertek vagy nagyon hosszúak. Internetes hivatkozások megadásakor fontos, hogy az elérési út előtt megadjuk az oldal tulajdonosát és tartalmát (mivel a link egy idő után akár elérhetetlenné is válhat), valamint az elérés időpontját.
|
||||||
|
|
||||||
|
\vspace{5mm}
|
||||||
|
Fontos:
|
||||||
|
\begin{itemize}
|
||||||
|
\item A szakdolgozatkészítő / diplomatervező nyilatkozata (a jelen sablonban szereplő szövegtartalommal) kötelező előírás, Karunkon ennek hiányában a szakdolgozat/diplomaterv nem bírálható és nem védhető!
|
||||||
|
\item Mind a dolgozat, mind a melléklet maximálisan 15~MB méretű lehet!
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\vspace{5mm}
|
||||||
|
\begin{center}
|
||||||
|
Jó munkát, sikeres szakdolgozatkészítést, ill. diplomatervezést kívánunk!
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\normalsize
|
||||||
|
\selectthesislanguage
|
65
docs/thesis/include/packages.tex
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
% thanks to http://tex.stackexchange.com/a/47579/71109
|
||||||
|
\usepackage{ifxetex}
|
||||||
|
\usepackage{ifluatex}
|
||||||
|
\newif\ifxetexorluatex % a new conditional starts as false
|
||||||
|
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi>0
|
||||||
|
\xetexorluatextrue
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\ifxetexorluatex
|
||||||
|
\usepackage{fontspec}
|
||||||
|
\else
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[lighttt]{lmodern}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\usepackage[english,magyar]{babel} % Alapértelmezés szerint utoljára definiált nyelv lesz aktív, de később külön beállítjuk az aktív nyelvet.
|
||||||
|
|
||||||
|
%\usepackage{cmap}
|
||||||
|
\usepackage{amsfonts,amsmath,amssymb} % Mathematical symbols.
|
||||||
|
%\usepackage[ruled,boxed,resetcount,linesnumbered]{algorithm2e} % For pseudocodes. % beware: this is not compatible with LuaLaTeX, see http://tex.stackexchange.com/questions/34814/lualatex-and-algorithm2e
|
||||||
|
\usepackage{booktabs} % For publication quality tables for LaTeX
|
||||||
|
\usepackage{graphicx}
|
||||||
|
|
||||||
|
%\usepackage{fancyhdr}
|
||||||
|
%\usepackage{lastpage}
|
||||||
|
|
||||||
|
\usepackage{anysize}
|
||||||
|
%\usepackage{sectsty}
|
||||||
|
\usepackage{setspace} % For setting line spacing
|
||||||
|
|
||||||
|
\usepackage[unicode]{hyperref} % For hyperlinks in the generated document.
|
||||||
|
\usepackage{xcolor}
|
||||||
|
\usepackage{listings} % For source code snippets.
|
||||||
|
\lstdefinestyle{sharpc}{language=[Sharp]C, frame=lr, rulecolor=\color{blue!80!black}}
|
||||||
|
|
||||||
|
\usepackage[amsmath,thmmarks]{ntheorem} % Theorem-like environments.
|
||||||
|
|
||||||
|
\usepackage[hang]{caption}
|
||||||
|
|
||||||
|
\onehalfspacing
|
||||||
|
|
||||||
|
\newcommand{\selecthungarian}{
|
||||||
|
\selectlanguage{magyar}
|
||||||
|
\setlength{\parindent}{2em}
|
||||||
|
\setlength{\parskip}{0em}
|
||||||
|
\frenchspacing
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\selectenglish}{
|
||||||
|
\selectlanguage{english}
|
||||||
|
\setlength{\parindent}{0em}
|
||||||
|
\setlength{\parskip}{0.5em}
|
||||||
|
\nonfrenchspacing
|
||||||
|
\renewcommand{\figureautorefname}{Figure}
|
||||||
|
\renewcommand{\tableautorefname}{Table}
|
||||||
|
\renewcommand{\partautorefname}{Part}
|
||||||
|
\renewcommand{\chapterautorefname}{Chapter}
|
||||||
|
\renewcommand{\sectionautorefname}{Section}
|
||||||
|
\renewcommand{\subsectionautorefname}{Section}
|
||||||
|
\renewcommand{\subsubsectionautorefname}{Section}
|
||||||
|
}
|
||||||
|
|
||||||
|
\usepackage[numbers]{natbib}
|
||||||
|
\usepackage{xspace}
|
122
docs/thesis/include/preamble.tex
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Page layout setup
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% we need to redefine the pagestyle plain
|
||||||
|
% another possibility is to use the body of this command without \fancypagestyle
|
||||||
|
% and use \pagestyle{fancy} but in that case the special pages
|
||||||
|
% (like the ToC, the References, and the Chapter pages)remain in plane style
|
||||||
|
|
||||||
|
\pagestyle{plain}
|
||||||
|
\marginsize{35mm}{25mm}{15mm}{15mm}
|
||||||
|
|
||||||
|
\setcounter{tocdepth}{3}
|
||||||
|
%\sectionfont{\large\upshape\bfseries}
|
||||||
|
\setcounter{secnumdepth}{3}
|
||||||
|
|
||||||
|
\sloppy % Margón túllógó sorok tiltása.
|
||||||
|
\widowpenalty=10000 \clubpenalty=10000 %A fattyú- és árvasorok elkerülése
|
||||||
|
\def\hyph{-\penalty0\hskip0pt\relax} % Kötőjeles szavak elválasztásának engedélyezése
|
||||||
|
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Setup hyperref package
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\hypersetup{
|
||||||
|
% bookmarks=true, % show bookmarks bar?
|
||||||
|
unicode=true, % non-Latin characters in Acrobat's bookmarks
|
||||||
|
pdftitle={\vikcim}, % title
|
||||||
|
pdfauthor={\szerzoMeta}, % author
|
||||||
|
pdfsubject={\vikdoktipus}, % subject of the document
|
||||||
|
pdfcreator={\szerzoMeta}, % creator of the document
|
||||||
|
pdfproducer={}, % producer of the document
|
||||||
|
pdfkeywords={}, % list of keywords (separate then by comma)
|
||||||
|
pdfnewwindow=true, % links in new window
|
||||||
|
colorlinks=true, % false: boxed links; true: colored links
|
||||||
|
linkcolor=black, % color of internal links
|
||||||
|
citecolor=black, % color of links to bibliography
|
||||||
|
filecolor=black, % color of file links
|
||||||
|
urlcolor=black % color of external links
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Set up listings
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\definecolor{lightgray}{rgb}{0.95,0.95,0.95}
|
||||||
|
\lstset{
|
||||||
|
basicstyle=\scriptsize\ttfamily, % print whole listing small
|
||||||
|
keywordstyle=\color{black}\bfseries, % bold black keywords
|
||||||
|
identifierstyle=, % nothing happens
|
||||||
|
% default behavior: comments in italic, to change use
|
||||||
|
% commentstyle=\color{green}, % for e.g. green comments
|
||||||
|
stringstyle=\scriptsize,
|
||||||
|
showstringspaces=false, % no special string spaces
|
||||||
|
aboveskip=3pt,
|
||||||
|
belowskip=3pt,
|
||||||
|
backgroundcolor=\color{lightgray},
|
||||||
|
columns=flexible,
|
||||||
|
keepspaces=true,
|
||||||
|
escapeinside={(*@}{@*)},
|
||||||
|
captionpos=b,
|
||||||
|
breaklines=true,
|
||||||
|
frame=single,
|
||||||
|
float=!ht,
|
||||||
|
tabsize=2,
|
||||||
|
literate=*
|
||||||
|
{á}{{\'a}}1 {é}{{\'e}}1 {í}{{\'i}}1 {ó}{{\'o}}1 {ö}{{\"o}}1 {ő}{{\H{o}}}1 {ú}{{\'u}}1 {ü}{{\"u}}1 {ű}{{\H{u}}}1
|
||||||
|
{Á}{{\'A}}1 {É}{{\'E}}1 {Í}{{\'I}}1 {Ó}{{\'O}}1 {Ö}{{\"O}}1 {Ő}{{\H{O}}}1 {Ú}{{\'U}}1 {Ü}{{\"U}}1 {Ű}{{\H{U}}}1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Set up theorem-like environments
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Using ntheorem package -- see http://www.math.washington.edu/tex-archive/macros/latex/contrib/ntheorem/ntheorem.pdf
|
||||||
|
|
||||||
|
\theoremstyle{plain}
|
||||||
|
\theoremseparator{.}
|
||||||
|
\newtheorem{example}{\pelda}
|
||||||
|
|
||||||
|
\theoremseparator{.}
|
||||||
|
%\theoremprework{\bigskip\hrule\medskip}
|
||||||
|
%\theorempostwork{\hrule\bigskip}
|
||||||
|
\theorembodyfont{\upshape}
|
||||||
|
\theoremsymbol{{\large \ensuremath{\centerdot}}}
|
||||||
|
\newtheorem{definition}{\definicio}
|
||||||
|
|
||||||
|
\theoremseparator{.}
|
||||||
|
%\theoremprework{\bigskip\hrule\medskip}
|
||||||
|
%\theorempostwork{\hrule\bigskip}
|
||||||
|
\newtheorem{theorem}{\tetel}
|
||||||
|
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Some new commands and declarations
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\newcommand{\code}[1]{{\upshape\ttfamily\scriptsize\indent #1}}
|
||||||
|
\newcommand{\doi}[1]{DOI: \href{http://dx.doi.org/\detokenize{#1}}{\raggedright{\texttt{\detokenize{#1}}}}} % A hivatkozások közt így könnyebb DOI-t megadni.
|
||||||
|
|
||||||
|
\DeclareMathOperator*{\argmax}{arg\,max}
|
||||||
|
%\DeclareMathOperator*[1]{\floor}{arg\,max}
|
||||||
|
\DeclareMathOperator{\sign}{sgn}
|
||||||
|
\DeclareMathOperator{\rot}{rot}
|
||||||
|
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Setup captions
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\captionsetup[figure]{
|
||||||
|
width=.75\textwidth,
|
||||||
|
aboveskip=10pt}
|
||||||
|
|
||||||
|
\renewcommand{\captionlabelfont}{\bf}
|
||||||
|
%\renewcommand{\captionfont}{\footnotesize\it}
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Hyphenation exceptions
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\hyphenation{Shakes-peare Mar-seilles ár-víz-tű-rő tü-kör-fú-ró-gép}
|
||||||
|
|
||||||
|
|
||||||
|
\author{\vikszerzo}
|
||||||
|
\title{\viktitle}
|
10
docs/thesis/include/project.tex
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Feladatkiiras (a tanszeken atveheto, kinyomtatott valtozat)
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\clearpage
|
||||||
|
\begin{center}
|
||||||
|
\large
|
||||||
|
\textbf{FELADATKIÍRÁS}\\
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
A feladatkiírást a tanszéki adminisztrációban lehet átvenni, és a leadott munkába eredeti, tanszéki pecséttel ellátott és a tanszékvezető által aláírt lapot kell belefűzni (ezen oldal \emph{helyett}, ez az oldal csak útmutatás). Az elektronikusan feltöltött dolgozatban már nem kell beleszerkeszteni ezt a feladatkiírást.
|
11
docs/thesis/include/tdk-variables.tex
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% TDK-specifikus változók
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\newcommand{\tdkszerzoB}{Második Szerző} % Második szerző neve; hagyd üresen, ha egyedül írtad a TDK-t.
|
||||||
|
\newcommand{\tdkev}{2014} % A dolgozat írásának éve (pl. "2014") (Ez OTDK-nál eltérhet az aktuális évtől.)
|
||||||
|
|
||||||
|
% További adatok az OTDK címlaphoz (BME-s TDK-hoz nem kell kitölteni)
|
||||||
|
\newcommand{\tdkevfolyamA}{IV} % Első szerző évfolyama, római számmal (pl. IV).
|
||||||
|
\newcommand{\tdkevfolyamB}{III} % Második szerző évfolyama, római számmal (pl. III).
|
||||||
|
\newcommand{\tdkkonzulensbeosztasA}{egyetemi tanár} % Első konzulens beosztása (pl. egyetemi docens)
|
||||||
|
\newcommand{\tdkkonzulensbeosztasB}{doktorandusz} % Második konzulens beosztása (pl. egyetemi docens)
|
56
docs/thesis/include/thesis-en.tex
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Elnevezések
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\newcommand{\bme}{Budapest University of Technology and Economics}
|
||||||
|
\newcommand{\vik}{Faculty of Electrical Engineering and Informatics}
|
||||||
|
|
||||||
|
\newcommand{\bmetmit}{Department of Telecommunications and Media Informatics}
|
||||||
|
|
||||||
|
\newcommand{\keszitette}{Author}
|
||||||
|
\newcommand{\konzulens}{Advisor}
|
||||||
|
|
||||||
|
\newcommand{\bsc}{Bachelor's Thesis}
|
||||||
|
\newcommand{\msc}{Master's Thesis}
|
||||||
|
\newcommand{\tdk}{Scientific Students' Association Report}
|
||||||
|
\newcommand{\bsconlab}{BSc Project Laboratory}
|
||||||
|
\newcommand{\msconlabi}{MSc Project Laboratory 1}
|
||||||
|
\newcommand{\msconlabii}{MSc Project Laboratory 2}
|
||||||
|
|
||||||
|
\newcommand{\pelda}{Example}
|
||||||
|
\newcommand{\definicio}{Definition}
|
||||||
|
\newcommand{\tetel}{Theorem}
|
||||||
|
|
||||||
|
\newcommand{\bevezetes}{Introduction}
|
||||||
|
\newcommand{\koszonetnyilvanitas}{Acknowledgements}
|
||||||
|
\newcommand{\fuggelek}{Appendix}
|
||||||
|
|
||||||
|
% Optional custom titles
|
||||||
|
%\addto\captionsenglish{%
|
||||||
|
%\renewcommand*{\listfigurename}{Your list of figures title}
|
||||||
|
%\renewcommand*{\listtablename}{Your list of tables title}
|
||||||
|
%\renewcommand*{\bibname}{Your bibliography title}
|
||||||
|
%}
|
||||||
|
|
||||||
|
\newcommand{\szerzo}{\vikszerzoKeresztnev{} \vikszerzoVezeteknev}
|
||||||
|
\newcommand{\vikkonzulensA}{\vikkonzulensAMegszolitas\vikkonzulensAKeresztnev{} \vikkonzulensAVezeteknev}
|
||||||
|
\newcommand{\vikkonzulensB}{\vikkonzulensBMegszolitas\vikkonzulensBKeresztnev{} \vikkonzulensBVezeteknev}
|
||||||
|
\newcommand{\vikkonzulensC}{\vikkonzulensCMegszolitas\vikkonzulensCKeresztnev{} \vikkonzulensCVezeteknev}
|
||||||
|
|
||||||
|
\newcommand{\selectthesislanguage}{\selectenglish}
|
||||||
|
|
||||||
|
\bibliographystyle{plainnat}
|
||||||
|
|
||||||
|
\newcommand{\ie}{i.e.\@\xspace}
|
||||||
|
\newcommand{\Ie}{I.e.\@\xspace}
|
||||||
|
\newcommand{\eg}{e.g.\@\xspace}
|
||||||
|
\newcommand{\Eg}{E.g.\@\xspace}
|
||||||
|
\newcommand{\etal}{et al.\@\xspace}
|
||||||
|
\newcommand{\etc}{etc.\@\xspace}
|
||||||
|
\newcommand{\vs}{vs.\@\xspace}
|
||||||
|
\newcommand{\viz}{viz.\@\xspace} % videlicet
|
||||||
|
\newcommand{\cf}{cf.\@\xspace} % confer
|
||||||
|
\newcommand{\Cf}{Cf.\@\xspace}
|
||||||
|
\newcommand{\wrt}{w.r.t.\@\xspace} % with respect to
|
||||||
|
\newcommand{\approximately}{approx.\@\xspace}
|
||||||
|
|
||||||
|
\newcommand{\appendixnumber}{1} % a fofejezet-szamlalo az angol ABC 1. betuje (A) lesz
|
45
docs/thesis/include/thesis-hu.tex
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Elnevezések
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\newcommand{\bme}{Budapesti Műszaki és Gazdaságtudományi Egyetem}
|
||||||
|
\newcommand{\vik}{Villamosmérnöki és Informatikai Kar}
|
||||||
|
|
||||||
|
\newcommand{\bmetmit}{Távközlési és Médiainformatikai Tanszék}
|
||||||
|
|
||||||
|
\newcommand{\keszitette}{Készítette}
|
||||||
|
\newcommand{\konzulens}{Konzulens}
|
||||||
|
|
||||||
|
\newcommand{\bsc}{Szakdolgozat}
|
||||||
|
\newcommand{\msc}{Diplomaterv}
|
||||||
|
\newcommand{\tdk}{TDK dolgozat}
|
||||||
|
\newcommand{\bsconlab}{BSc Önálló laboratórium}
|
||||||
|
\newcommand{\msconlabi}{MSc Önálló laboratórium 1.}
|
||||||
|
\newcommand{\msconlabii}{MSc Önálló laboratórium 2.}
|
||||||
|
|
||||||
|
\newcommand{\pelda}{Példa}
|
||||||
|
\newcommand{\definicio}{Definíció}
|
||||||
|
\newcommand{\tetel}{Tétel}
|
||||||
|
|
||||||
|
\newcommand{\bevezetes}{Bevezetés}
|
||||||
|
\newcommand{\koszonetnyilvanitas}{Köszönetnyilvánítás}
|
||||||
|
\newcommand{\fuggelek}{Függelék}
|
||||||
|
|
||||||
|
% Opcionálisan átnevezhető címek
|
||||||
|
%\addto\captionsmagyar{%
|
||||||
|
%\renewcommand{\listfigurename}{Saját ábrajegyzék cím}
|
||||||
|
%\renewcommand{\listtablename}{Saját táblázatjegyzék cím}
|
||||||
|
%\renewcommand{\bibname}{Saját irodalomjegyzék név}
|
||||||
|
%}
|
||||||
|
|
||||||
|
\newcommand{\szerzo}{\vikszerzoVezeteknev{} \vikszerzoKeresztnev}
|
||||||
|
\newcommand{\vikkonzulensA}{\vikkonzulensAMegszolitas\vikkonzulensAVezeteknev{} \vikkonzulensAKeresztnev}
|
||||||
|
\newcommand{\vikkonzulensB}{\vikkonzulensBMegszolitas\vikkonzulensBVezeteknev{} \vikkonzulensBKeresztnev}
|
||||||
|
\newcommand{\vikkonzulensC}{\vikkonzulensCMegszolitas\vikkonzulensCVezeteknev{} \vikkonzulensCKeresztnev}
|
||||||
|
|
||||||
|
\newcommand{\selectthesislanguage}{\selecthungarian}
|
||||||
|
|
||||||
|
\bibliographystyle{huplain}
|
||||||
|
|
||||||
|
\def\lstlistingname{lista}
|
||||||
|
|
||||||
|
\newcommand{\appendixnumber}{6} % a fofejezet-szamlalo az angol ABC 6. betuje (F) lesz
|
58
docs/thesis/include/titlepage-otdk.tex
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
%% OTDK külső címlap
|
||||||
|
\begin{titlepage}
|
||||||
|
$\;$
|
||||||
|
\vspace{5cm}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\Huge
|
||||||
|
\textbf{TDK-dolgozat}\let\thefootnote\relax\footnote{A dolgozat bemutatását a XXXXXXXXX ``Lorem ipsum dolor sit amet'' című program támogatta.}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\vspace{13cm}
|
||||||
|
|
||||||
|
\Large
|
||||||
|
\hspace{8cm} \szerzo
|
||||||
|
|
||||||
|
\hspace{8cm} \tdkszerzoB
|
||||||
|
|
||||||
|
\hspace{8cm} \tdkev.
|
||||||
|
\end{titlepage}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\thispagestyle{empty}
|
||||||
|
|
||||||
|
|
||||||
|
%% OTDK belső címlap
|
||||||
|
\begin{titlepage}
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=7cm]{./figures/bme_logo.pdf}
|
||||||
|
\vspace{0.3cm}
|
||||||
|
|
||||||
|
\bme \\
|
||||||
|
\vik \\
|
||||||
|
\viktanszek \\
|
||||||
|
\vspace{3.5cm}
|
||||||
|
|
||||||
|
\huge {\vikcim}
|
||||||
|
\vspace{1.5cm}
|
||||||
|
|
||||||
|
\large {\textbf{\vikdoktipus}}
|
||||||
|
\vfill
|
||||||
|
|
||||||
|
{\Large
|
||||||
|
{\large \keszitette:} \\ \vspace{0.2cm}
|
||||||
|
\szerzo \\ \tdkevfolyamA. évfolyam \\
|
||||||
|
\vspace{0.5cm}
|
||||||
|
\tdkszerzoB \\ \tdkevfolyamB. évfolyam \\
|
||||||
|
\vspace{1.5cm}
|
||||||
|
{\large \konzulens:} \\ \vspace{0.2cm}
|
||||||
|
\vikkonzulensA,\\ \tdkkonzulensbeosztasA \\
|
||||||
|
\vspace{0.5cm}
|
||||||
|
\vikkonzulensB,\\ \tdkkonzulensbeosztasB \\
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace{2cm}
|
||||||
|
\large {\tdkev.}
|
||||||
|
|
||||||
|
\end{center}
|
||||||
|
\end{titlepage}
|
32
docs/thesis/include/titlepage-tdk.tex
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
%% TDK címlap
|
||||||
|
\begin{titlepage}
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=7cm]{./figures/bme_logo.pdf}
|
||||||
|
\vspace{0.3cm}
|
||||||
|
|
||||||
|
\bme \\
|
||||||
|
\vik \\
|
||||||
|
\viktanszek \\
|
||||||
|
\vspace{5cm}
|
||||||
|
|
||||||
|
\huge {\vikcim}
|
||||||
|
\vspace{1.5cm}
|
||||||
|
|
||||||
|
\large {\textbf{\tdk}}
|
||||||
|
\vfill
|
||||||
|
|
||||||
|
{\Large
|
||||||
|
\keszitette: \\ \vspace{0.3cm}
|
||||||
|
\szerzo \\
|
||||||
|
\tdkszerzoB \\
|
||||||
|
\vspace{1.5cm}
|
||||||
|
\konzulens: \\ \vspace{0.3cm}
|
||||||
|
\vikkonzulensA \\
|
||||||
|
\vikkonzulensB \\
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace{2cm}
|
||||||
|
\large {\tdkev}
|
||||||
|
\end{center}
|
||||||
|
\end{titlepage}
|
||||||
|
%% Címlap vége
|
44
docs/thesis/include/titlepage.aux
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\@setckpt{include/titlepage}{
|
||||||
|
\setcounter{page}{1}
|
||||||
|
\setcounter{equation}{0}
|
||||||
|
\setcounter{enumi}{0}
|
||||||
|
\setcounter{enumii}{0}
|
||||||
|
\setcounter{enumiii}{0}
|
||||||
|
\setcounter{enumiv}{0}
|
||||||
|
\setcounter{footnote}{0}
|
||||||
|
\setcounter{mpfootnote}{0}
|
||||||
|
\setcounter{part}{0}
|
||||||
|
\setcounter{chapter}{0}
|
||||||
|
\setcounter{section}{0}
|
||||||
|
\setcounter{subsection}{0}
|
||||||
|
\setcounter{subsubsection}{0}
|
||||||
|
\setcounter{paragraph}{0}
|
||||||
|
\setcounter{subparagraph}{0}
|
||||||
|
\setcounter{figure}{0}
|
||||||
|
\setcounter{table}{0}
|
||||||
|
\setcounter{footnote@add}{0}
|
||||||
|
\setcounter{footnote@ch}{0}
|
||||||
|
\setcounter{parentequation}{0}
|
||||||
|
\setcounter{Item}{0}
|
||||||
|
\setcounter{Hfootnote}{0}
|
||||||
|
\setcounter{bookmark@seq@number}{0}
|
||||||
|
\setcounter{lstnumber}{1}
|
||||||
|
\setcounter{endNonectr}{1}
|
||||||
|
\setcounter{currNonectr}{0}
|
||||||
|
\setcounter{caption@flags}{0}
|
||||||
|
\setcounter{continuedfloat}{0}
|
||||||
|
\setcounter{NAT@ctr}{0}
|
||||||
|
\setcounter{currexamplectr}{0}
|
||||||
|
\setcounter{endexamplectr}{0}
|
||||||
|
\setcounter{example}{0}
|
||||||
|
\setcounter{currdefinitionctr}{0}
|
||||||
|
\setcounter{enddefinitionctr}{0}
|
||||||
|
\setcounter{definition}{0}
|
||||||
|
\setcounter{currtheoremctr}{0}
|
||||||
|
\setcounter{endtheoremctr}{0}
|
||||||
|
\setcounter{theorem}{0}
|
||||||
|
\setcounter{section@level}{0}
|
||||||
|
\setcounter{lstlisting}{0}
|
||||||
|
}
|
33
docs/thesis/include/titlepage.tex
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
\hypersetup{pageanchor=false}
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% The title page
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\begin{titlepage}
|
||||||
|
\begin{center}
|
||||||
|
\includegraphics[width=60mm,keepaspectratio]{figures/bme_logo.pdf}\\
|
||||||
|
\vspace{0.3cm}
|
||||||
|
\textbf{\bme}\\
|
||||||
|
\textmd{\vik}\\
|
||||||
|
\textmd{\viktanszek}\\[5cm]
|
||||||
|
|
||||||
|
\vspace{0.4cm}
|
||||||
|
{\huge \bfseries \vikcim}\\[0.8cm]
|
||||||
|
\vspace{0.5cm}
|
||||||
|
\textsc{\Large \vikdoktipus}\\[4cm]
|
||||||
|
|
||||||
|
{
|
||||||
|
\renewcommand{\arraystretch}{0.85}
|
||||||
|
\begin{tabular}{cc}
|
||||||
|
\makebox[7cm]{\emph{\keszitette}} & \makebox[7cm]{\emph{\konzulens}} \\ \noalign{\smallskip}
|
||||||
|
\makebox[7cm]{\szerzo} & \makebox[7cm]{\vikkonzulensA} \\
|
||||||
|
& \makebox[7cm]{\vikkonzulensB} \\
|
||||||
|
& \makebox[7cm]{\vikkonzulensC} \\
|
||||||
|
\end{tabular}
|
||||||
|
}
|
||||||
|
|
||||||
|
\vfill
|
||||||
|
{\large \today}
|
||||||
|
\end{center}
|
||||||
|
\end{titlepage}
|
||||||
|
\hypersetup{pageanchor=false}
|
||||||
|
|
731
docs/thesis/lua/language.dat.lua
Normal file
@ -0,0 +1,731 @@
|
|||||||
|
-- Generated by ./install-tl on Sun Feb 12 19:17:26 2017
|
||||||
|
-- $Id: language.us.lua 18737 2010-06-04 17:09:02Z karl $
|
||||||
|
--[[
|
||||||
|
language.us.dat (and the start of language.dat.lua), used by:
|
||||||
|
- a special luatex version of hyphen.cfg (derived from the babel system);
|
||||||
|
- a special luatex version of etex.src (from the e-TeX distributon).
|
||||||
|
|
||||||
|
See luatex-hyphen.pdf (currently part of the hyph-utf8 package) for details.
|
||||||
|
|
||||||
|
DO NOT EDIT THIS FILE (language.dat.lua)! It is generated by tlmgr.
|
||||||
|
See language.dat (or language.us) for more information.
|
||||||
|
|
||||||
|
Warning: formats using this file also use one of language.dat or
|
||||||
|
language.def. Update them accordingly. The interaction between these
|
||||||
|
files is documented in luatex-hyphen.pdf, but here is a summary:
|
||||||
|
- a language must be mentioned in language.dat or language.def to be
|
||||||
|
available; if, in addition, it is:
|
||||||
|
- not mentioned in language.dat.lua, then it is dumped in the format;
|
||||||
|
- mentioned in language.dat.lua with a key special="disabled:<reason>",
|
||||||
|
then it is not available at all;
|
||||||
|
- mentioned in language.dat.lua with a normal entry, then it will not
|
||||||
|
be dumped in the format, but loaded at runtime when activated.
|
||||||
|
]]
|
||||||
|
|
||||||
|
return {
|
||||||
|
["english"]={
|
||||||
|
loader="hyphen.tex",
|
||||||
|
special="language0", -- should be dumped in the format
|
||||||
|
lefthyphenmin=2,
|
||||||
|
righthyphenmin=3,
|
||||||
|
synonyms={"usenglish","USenglish","american"},
|
||||||
|
},
|
||||||
|
-- dumylang and zerohyph are dumped in the format,
|
||||||
|
-- since they contain either very few or no patterns at all
|
||||||
|
-- END of language.us.lua (missing '}' appended after all entries)
|
||||||
|
-- from dehyph-exptl:
|
||||||
|
['german-x-2014-05-21'] = {
|
||||||
|
loader = 'dehypht-x-2014-05-21.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'german-x-latest' },
|
||||||
|
patterns = 'hyph-de-1901.pat.txt',
|
||||||
|
hyphenation = 'hyph-de-1901.hyp.txt',
|
||||||
|
},
|
||||||
|
['ngerman-x-2014-05-21'] = {
|
||||||
|
loader = 'dehyphn-x-2014-05-21.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'ngerman-x-latest' },
|
||||||
|
patterns = 'hyph-de-1996.pat.txt',
|
||||||
|
hyphenation = 'hyph-de-1996.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-afrikaans:
|
||||||
|
['afrikaans'] = {
|
||||||
|
loader = 'loadhyph-af.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-af.pat.txt',
|
||||||
|
hyphenation = 'hyph-af.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-ancientgreek:
|
||||||
|
['ancientgreek'] = {
|
||||||
|
loader = 'loadhyph-grc.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-grc.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['ibycus'] = {
|
||||||
|
loader = 'ibyhyph.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
special = 'disabled:8-bit only',
|
||||||
|
},
|
||||||
|
-- from hyphen-arabic:
|
||||||
|
['arabic'] = {
|
||||||
|
loader = 'zerohyph.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-armenian:
|
||||||
|
['armenian'] = {
|
||||||
|
loader = 'loadhyph-hy.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-hy.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-basque:
|
||||||
|
['basque'] = {
|
||||||
|
loader = 'loadhyph-eu.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-eu.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-bulgarian:
|
||||||
|
['bulgarian'] = {
|
||||||
|
loader = 'loadhyph-bg.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-bg.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-catalan:
|
||||||
|
['catalan'] = {
|
||||||
|
loader = 'loadhyph-ca.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ca.pat.txt',
|
||||||
|
hyphenation = 'hyph-ca.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-chinese:
|
||||||
|
['pinyin'] = {
|
||||||
|
loader = 'loadhyph-zh-latn-pinyin.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-zh-latn-pinyin.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-churchslavonic:
|
||||||
|
['churchslavonic'] = {
|
||||||
|
loader = 'loadhyph-cu.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-cu.pat.txt',
|
||||||
|
hyphenation = 'hyph-cu.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-coptic:
|
||||||
|
['coptic'] = {
|
||||||
|
loader = 'loadhyph-cop.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-cop.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-croatian:
|
||||||
|
['croatian'] = {
|
||||||
|
loader = 'loadhyph-hr.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-hr.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-czech:
|
||||||
|
['czech'] = {
|
||||||
|
loader = 'loadhyph-cs.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-cs.pat.txt',
|
||||||
|
hyphenation = 'hyph-cs.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-danish:
|
||||||
|
['danish'] = {
|
||||||
|
loader = 'loadhyph-da.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-da.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-dutch:
|
||||||
|
['dutch'] = {
|
||||||
|
loader = 'loadhyph-nl.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-nl.pat.txt',
|
||||||
|
hyphenation = 'hyph-nl.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-english:
|
||||||
|
['ukenglish'] = {
|
||||||
|
loader = 'loadhyph-en-gb.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { 'british', 'UKenglish' },
|
||||||
|
patterns = 'hyph-en-gb.pat.txt',
|
||||||
|
hyphenation = 'hyph-en-gb.hyp.txt',
|
||||||
|
},
|
||||||
|
['usenglishmax'] = {
|
||||||
|
loader = 'loadhyph-en-us.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-en-us.pat.txt',
|
||||||
|
hyphenation = 'hyph-en-us.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-esperanto:
|
||||||
|
['esperanto'] = {
|
||||||
|
loader = 'loadhyph-eo.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-eo.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-estonian:
|
||||||
|
['estonian'] = {
|
||||||
|
loader = 'loadhyph-et.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-et.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-ethiopic:
|
||||||
|
['ethiopic'] = {
|
||||||
|
loader = 'loadhyph-mul-ethi.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { 'amharic', 'geez' },
|
||||||
|
patterns = 'hyph-mul-ethi.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-farsi:
|
||||||
|
['farsi'] = {
|
||||||
|
loader = 'zerohyph.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { 'persian' },
|
||||||
|
patterns = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-finnish:
|
||||||
|
['finnish'] = {
|
||||||
|
loader = 'loadhyph-fi.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-fi.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-french:
|
||||||
|
['french'] = {
|
||||||
|
loader = 'loadhyph-fr.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'patois', 'francais' },
|
||||||
|
patterns = 'hyph-fr.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-friulan:
|
||||||
|
['friulan'] = {
|
||||||
|
loader = 'loadhyph-fur.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-fur.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-galician:
|
||||||
|
['galician'] = {
|
||||||
|
loader = 'loadhyph-gl.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-gl.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-georgian:
|
||||||
|
['georgian'] = {
|
||||||
|
loader = 'loadhyph-ka.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ka.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-german:
|
||||||
|
['german'] = {
|
||||||
|
loader = 'loadhyph-de-1901.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-de-1901.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['ngerman'] = {
|
||||||
|
loader = 'loadhyph-de-1996.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-de-1996.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['swissgerman'] = {
|
||||||
|
loader = 'loadhyph-de-ch-1901.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-de-ch-1901.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-greek:
|
||||||
|
['monogreek'] = {
|
||||||
|
loader = 'loadhyph-el-monoton.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-el-monoton.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['greek'] = {
|
||||||
|
loader = 'loadhyph-el-polyton.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { 'polygreek' },
|
||||||
|
patterns = 'hyph-el-polyton.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-hungarian:
|
||||||
|
['hungarian'] = {
|
||||||
|
loader = 'loadhyph-hu.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'hungarian', 'magyar' },
|
||||||
|
patterns = 'hyph-hu.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-icelandic:
|
||||||
|
['icelandic'] = {
|
||||||
|
loader = 'loadhyph-is.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-is.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-indic:
|
||||||
|
['assamese'] = {
|
||||||
|
loader = 'loadhyph-as.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-as.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['bengali'] = {
|
||||||
|
loader = 'loadhyph-bn.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-bn.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['gujarati'] = {
|
||||||
|
loader = 'loadhyph-gu.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-gu.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['hindi'] = {
|
||||||
|
loader = 'loadhyph-hi.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-hi.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['kannada'] = {
|
||||||
|
loader = 'loadhyph-kn.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-kn.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['malayalam'] = {
|
||||||
|
loader = 'loadhyph-ml.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ml.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['marathi'] = {
|
||||||
|
loader = 'loadhyph-mr.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-mr.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['oriya'] = {
|
||||||
|
loader = 'loadhyph-or.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-or.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['panjabi'] = {
|
||||||
|
loader = 'loadhyph-pa.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-pa.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['tamil'] = {
|
||||||
|
loader = 'loadhyph-ta.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ta.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['telugu'] = {
|
||||||
|
loader = 'loadhyph-te.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 1,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-te.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-indonesian:
|
||||||
|
['indonesian'] = {
|
||||||
|
loader = 'loadhyph-id.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-id.pat.txt',
|
||||||
|
hyphenation = 'hyph-id.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-interlingua:
|
||||||
|
['interlingua'] = {
|
||||||
|
loader = 'loadhyph-ia.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ia.pat.txt',
|
||||||
|
hyphenation = 'hyph-ia.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-irish:
|
||||||
|
['irish'] = {
|
||||||
|
loader = 'loadhyph-ga.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ga.pat.txt',
|
||||||
|
hyphenation = 'hyph-ga.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-italian:
|
||||||
|
['italian'] = {
|
||||||
|
loader = 'loadhyph-it.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-it.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-kurmanji:
|
||||||
|
['kurmanji'] = {
|
||||||
|
loader = 'loadhyph-kmr.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-kmr.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-latin:
|
||||||
|
['latin'] = {
|
||||||
|
loader = 'loadhyph-la.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-la.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['classiclatin'] = {
|
||||||
|
loader = 'loadhyph-la-x-classic.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-la-x-classic.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['liturgicallatin'] = {
|
||||||
|
loader = 'loadhyph-la-x-liturgic.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-la-x-liturgic.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-latvian:
|
||||||
|
['latvian'] = {
|
||||||
|
loader = 'loadhyph-lv.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-lv.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-lithuanian:
|
||||||
|
['lithuanian'] = {
|
||||||
|
loader = 'loadhyph-lt.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-lt.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-mongolian:
|
||||||
|
['mongolian'] = {
|
||||||
|
loader = 'loadhyph-mn-cyrl.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-mn-cyrl.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
['mongolianlmc'] = {
|
||||||
|
loader = 'loadhyph-mn-cyrl-x-lmc.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
special = 'disabled:only for 8bit montex with lmc encoding',
|
||||||
|
},
|
||||||
|
-- from hyphen-norwegian:
|
||||||
|
['bokmal'] = {
|
||||||
|
loader = 'loadhyph-nb.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'norwegian', 'norsk' },
|
||||||
|
patterns = 'hyph-nb.pat.txt',
|
||||||
|
hyphenation = 'hyph-nb.hyp.txt',
|
||||||
|
},
|
||||||
|
['nynorsk'] = {
|
||||||
|
loader = 'loadhyph-nn.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-nn.pat.txt',
|
||||||
|
hyphenation = 'hyph-nn.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-occitan:
|
||||||
|
['occitan'] = {
|
||||||
|
loader = 'loadhyph-oc.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-oc.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-piedmontese:
|
||||||
|
['piedmontese'] = {
|
||||||
|
loader = 'loadhyph-pms.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-pms.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-polish:
|
||||||
|
['polish'] = {
|
||||||
|
loader = 'loadhyph-pl.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-pl.pat.txt',
|
||||||
|
hyphenation = 'hyph-pl.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-portuguese:
|
||||||
|
['portuguese'] = {
|
||||||
|
loader = 'loadhyph-pt.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { 'portuges' },
|
||||||
|
patterns = 'hyph-pt.pat.txt',
|
||||||
|
hyphenation = 'hyph-pt.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-romanian:
|
||||||
|
['romanian'] = {
|
||||||
|
loader = 'loadhyph-ro.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ro.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-romansh:
|
||||||
|
['romansh'] = {
|
||||||
|
loader = 'loadhyph-rm.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-rm.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-russian:
|
||||||
|
['russian'] = {
|
||||||
|
loader = 'loadhyph-ru.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-ru.pat.txt',
|
||||||
|
hyphenation = 'hyph-ru.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-sanskrit:
|
||||||
|
['sanskrit'] = {
|
||||||
|
loader = 'loadhyph-sa.tex',
|
||||||
|
lefthyphenmin = 1,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-sa.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-serbian:
|
||||||
|
['serbian'] = {
|
||||||
|
loader = 'loadhyph-sr-latn.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-sh-latn.pat.txt,hyph-sh-cyrl.pat.txt',
|
||||||
|
hyphenation = 'hyph-sh-latn.hyp.txt,hyph-sh-cyrl.hyp.txt',
|
||||||
|
},
|
||||||
|
['serbianc'] = {
|
||||||
|
loader = 'loadhyph-sr-cyrl.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-sh-latn.pat.txt,hyph-sh-cyrl.pat.txt',
|
||||||
|
hyphenation = 'hyph-sh-latn.hyp.txt,hyph-sh-cyrl.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-slovak:
|
||||||
|
['slovak'] = {
|
||||||
|
loader = 'loadhyph-sk.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-sk.pat.txt',
|
||||||
|
hyphenation = 'hyph-sk.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-slovenian:
|
||||||
|
['slovenian'] = {
|
||||||
|
loader = 'loadhyph-sl.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'slovene' },
|
||||||
|
patterns = 'hyph-sl.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-spanish:
|
||||||
|
['spanish'] = {
|
||||||
|
loader = 'loadhyph-es.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { 'espanol' },
|
||||||
|
patterns = 'hyph-es.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-swedish:
|
||||||
|
['swedish'] = {
|
||||||
|
loader = 'loadhyph-sv.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-sv.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-thai:
|
||||||
|
['thai'] = {
|
||||||
|
loader = 'loadhyph-th.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-th.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-turkish:
|
||||||
|
['turkish'] = {
|
||||||
|
loader = 'loadhyph-tr.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-tr.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-turkmen:
|
||||||
|
['turkmen'] = {
|
||||||
|
loader = 'loadhyph-tk.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-tk.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-ukrainian:
|
||||||
|
['ukrainian'] = {
|
||||||
|
loader = 'loadhyph-uk.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-uk.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
-- from hyphen-uppersorbian:
|
||||||
|
['uppersorbian'] = {
|
||||||
|
loader = 'loadhyph-hsb.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 2,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-hsb.pat.txt',
|
||||||
|
hyphenation = 'hyph-hsb.hyp.txt',
|
||||||
|
},
|
||||||
|
-- from hyphen-welsh:
|
||||||
|
['welsh'] = {
|
||||||
|
loader = 'loadhyph-cy.tex',
|
||||||
|
lefthyphenmin = 2,
|
||||||
|
righthyphenmin = 3,
|
||||||
|
synonyms = { },
|
||||||
|
patterns = 'hyph-cy.pat.txt',
|
||||||
|
hyphenation = '',
|
||||||
|
},
|
||||||
|
}
|
8
docs/thesis/manual_build.bat
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
set DOCUMENT=thesis
|
||||||
|
|
||||||
|
pdflatex --quiet %DOCUMENT%
|
||||||
|
bibtex -quiet %DOCUMENT%
|
||||||
|
pdflatex --quiet %DOCUMENT%
|
||||||
|
pdflatex --quiet %DOCUMENT%
|
||||||
|
|
||||||
|
del /s *.aux *.dvi *.thm *.lof *.log *.lot *.fls *.out *.toc *.bbl *.blg
|
30
docs/thesis/thesis.aux
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
\relax
|
||||||
|
\providecommand\hyper@newdestlabel[2]{}
|
||||||
|
\providecommand\babel@aux[2]{}
|
||||||
|
\@nameuse{bbl@beforestart}
|
||||||
|
\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument}
|
||||||
|
\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined
|
||||||
|
\global\let\oldcontentsline\contentsline
|
||||||
|
\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}}
|
||||||
|
\global\let\oldnewlabel\newlabel
|
||||||
|
\gdef\newlabel#1#2{\newlabelxx{#1}#2}
|
||||||
|
\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}}
|
||||||
|
\AtEndDocument{\ifx\hyper@anchor\@undefined
|
||||||
|
\let\contentsline\oldcontentsline
|
||||||
|
\let\newlabel\oldnewlabel
|
||||||
|
\fi}
|
||||||
|
\fi}
|
||||||
|
\global\let\hyper@last\relax
|
||||||
|
\gdef\HyperFirstAtBeginDocument#1{#1}
|
||||||
|
\providecommand\HyField@AuxAddToFields[1]{}
|
||||||
|
\providecommand\HyField@AuxAddToCoFields[2]{}
|
||||||
|
\bibstyle{huplain}
|
||||||
|
\catcode \string ``=12
|
||||||
|
\@input{include/titlepage.aux}
|
||||||
|
\@input{include/declaration.aux}
|
||||||
|
\@input{content/abstract.aux}
|
||||||
|
\@input{content/introduction.aux}
|
||||||
|
\@input{content/birdnetes-introduction.aux}
|
||||||
|
\@input{content/birdmap-introduction.aux}
|
||||||
|
\@input{content/birdmap-technologies.aux}
|
||||||
|
\@input{content/birdmap-backend.aux}
|
51
docs/thesis/thesis.out
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
\BOOKMARK [0][-]{chapter*.2}{\376\377\000K\000i\000v\000o\000n\000a\000t}{}% 1
|
||||||
|
\BOOKMARK [0][-]{chapter*.3}{\376\377\000A\000b\000s\000t\000r\000a\000c\000t}{}% 2
|
||||||
|
\BOOKMARK [0][-]{chapter.1}{\376\377\000B\000e\000v\000e\000z\000e\000t\000\351\000s}{}% 3
|
||||||
|
\BOOKMARK [1][-]{section.1.1}{\376\377\000A\000\040\000p\000r\000o\000b\000l\000\351\000m\000a}{chapter.1}% 4
|
||||||
|
\BOOKMARK [1][-]{section.1.2}{\376\377\000A\000\040\000m\000e\000g\000o\000l\000d\000\341\000s}{chapter.1}% 5
|
||||||
|
\BOOKMARK [1][-]{section.1.3}{\376\377\000A\000\040\000s\000z\000a\000k\000d\000o\000l\000g\000o\000z\000a\000t\000\040\000f\000e\000l\000\351\000p\000\355\000t\000\351\000s\000e}{chapter.1}% 6
|
||||||
|
\BOOKMARK [0][-]{chapter.2}{\376\377\000A\000\040\000B\000i\000r\000d\000n\000e\000t\000e\000s\000\040\000b\000e\000m\000u\000t\000a\000t\000\341\000s\000a}{}% 7
|
||||||
|
\BOOKMARK [1][-]{section.2.1}{\376\377\000G\000y\000o\000r\000s\000\040\000e\000l\000m\000\351\000l\000e\000t\000i\000\040\000\366\000s\000s\000z\000e\000f\000o\000g\000l\000a\000l\000\363}{chapter.2}% 8
|
||||||
|
\BOOKMARK [2][-]{subsection.2.1.1}{\376\377\000C\000l\000o\000u\000d\000,\000\040\000f\000e\000l\000h\001\121}{section.2.1}% 9
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.1.1.1}{\376\377\000M\000i\000k\000r\000o\000s\000z\000o\000l\000g\000\341\000l\000t\000a\000t\000\341\000s\000o\000k}{subsection.2.1.1}% 10
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.1.1.2}{\376\377\000K\000o\000n\000t\000\351\000n\000e\000r\000e\000k}{subsection.2.1.1}% 11
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.1.1.3}{\376\377\000K\000u\000b\000e\000r\000n\000e\000t\000e\000s}{subsection.2.1.1}% 12
|
||||||
|
\BOOKMARK [2][-]{subsection.2.1.2}{\376\377\000M\000Q\000T\000T}{section.2.1}% 13
|
||||||
|
\BOOKMARK [2][-]{subsection.2.1.3}{\376\377\000O\000p\000e\000n\000\040\000A\000P\000I}{section.2.1}% 14
|
||||||
|
\BOOKMARK [1][-]{section.2.2}{\376\377\000R\000e\000n\000d\000s\000z\000e\000r\000s\000z\000i\000n\000t\001\161\000\040\000a\000r\000c\000h\000i\000t\000e\000k\000t\000\372\000r\000a}{chapter.2}% 15
|
||||||
|
\BOOKMARK [2][-]{subsection.2.2.1}{\376\377\000F\001\121\000b\000b\000\040\000k\000o\000m\000p\000o\000n\000e\000n\000s\000e\000k}{section.2.2}% 16
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.2.1.1}{\376\377\000I\000o\000T\000\040\000e\000s\000z\000k\000\366\000z\000\366\000k}{subsection.2.2.1}% 17
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.2.1.2}{\376\377\000I\000n\000p\000u\000t\000\040\000S\000e\000r\000v\000i\000c\000e}{subsection.2.2.1}% 18
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.2.1.3}{\376\377\000A\000I\000\040\000S\000e\000r\000v\000i\000c\000e}{subsection.2.2.1}% 19
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.2.1.4}{\376\377\000G\000u\000a\000r\000d\000\040\000S\000e\000r\000v\000i\000c\000e}{subsection.2.2.1}% 20
|
||||||
|
\BOOKMARK [3][-]{subsubsection.2.2.1.5}{\376\377\000C\000o\000m\000m\000a\000n\000d\000\040\000a\000n\000d\000\040\000C\000o\000n\000t\000r\000o\000l\000\040\000S\000e\000r\000v\000i\000c\000e}{subsection.2.2.1}% 21
|
||||||
|
\BOOKMARK [0][-]{chapter.3}{\376\377\000T\000e\000r\000v\000e\000k\000\040\000\351\000s\000\040\000a\000l\000t\000e\000r\000n\000a\000t\000\355\000v\000\341\000k}{}% 22
|
||||||
|
\BOOKMARK [1][-]{section.3.1}{\376\377\000T\000e\000r\000v\000e\000z\000\351\000s}{chapter.3}% 23
|
||||||
|
\BOOKMARK [1][-]{section.3.2}{\376\377\000A\000l\000t\000e\000r\000n\000a\000t\000\355\000v\000\341\000k}{chapter.3}% 24
|
||||||
|
\BOOKMARK [2][-]{subsection.3.2.1}{\376\377\000G\000r\000a\000f\000a\000n\000a}{section.3.2}% 25
|
||||||
|
\BOOKMARK [2][-]{subsection.3.2.2}{\376\377\000K\000i\000b\000a\000n\000a}{section.3.2}% 26
|
||||||
|
\BOOKMARK [2][-]{subsection.3.2.3}{\376\377\000K\000u\000b\000e\000r\000n\000e\000t\000e\000s\000\040\000D\000a\000s\000h\000b\000o\000a\000r\000d\000\040\000\050\000W\000e\000b\000\040\000U\000I\000\051}{section.3.2}% 27
|
||||||
|
\BOOKMARK [0][-]{chapter.4}{\376\377\000H\000a\000s\000z\000n\000\341\000l\000t\000\040\000t\000e\000c\000h\000n\000o\000l\000\363\000g\000i\000\341\000k}{}% 28
|
||||||
|
\BOOKMARK [1][-]{section.4.1}{\376\377\000A\000\040\000f\000e\000j\000l\000e\000s\000z\000t\000\351\000s\000i\000\040\000f\000o\000l\000y\000a\000m\000a\000t\000\040\000t\000e\000c\000h\000n\000o\000l\000\363\000g\000i\000\341\000i}{chapter.4}% 29
|
||||||
|
\BOOKMARK [2][-]{subsection.4.1.1}{\376\377\000G\000i\000t}{section.4.1}% 30
|
||||||
|
\BOOKMARK [2][-]{subsection.4.1.2}{\376\377\000T\000r\000e\000l\000l\000o}{section.4.1}% 31
|
||||||
|
\BOOKMARK [2][-]{subsection.4.1.3}{\376\377\000V\000i\000s\000u\000a\000l\000\040\000S\000t\000u\000d\000i\000o}{section.4.1}% 32
|
||||||
|
\BOOKMARK [2][-]{subsection.4.1.4}{\376\377\000V\000i\000s\000u\000a\000l\000\040\000S\000t\000u\000d\000i\000o\000\040\000C\000o\000d\000e}{section.4.1}% 33
|
||||||
|
\BOOKMARK [1][-]{section.4.2}{\376\377\000B\000a\000c\000k\000e\000n\000d\000\040\000t\000e\000c\000h\000n\000o\000l\000\363\000g\000i\000\341\000k}{chapter.4}% 34
|
||||||
|
\BOOKMARK [2][-]{subsection.4.2.1}{\376\377\000A\000S\000P\000.\000N\000E\000T\000\040\000C\000o\000r\000e}{section.4.2}% 35
|
||||||
|
\BOOKMARK [2][-]{subsection.4.2.2}{\376\377\000E\000n\000t\000i\000t\000y\000\040\000F\000r\000a\000m\000e\000w\000o\000r\000k\000\040\000C\000o\000r\000e}{section.4.2}% 36
|
||||||
|
\BOOKMARK [2][-]{subsection.4.2.3}{\376\377\000J\000S\000O\000N\000\040\000W\000e\000b\000\040\000T\000o\000k\000e\000n}{section.4.2}% 37
|
||||||
|
\BOOKMARK [2][-]{subsection.4.2.4}{\376\377\000S\000i\000g\000n\000a\000l\000R}{section.4.2}% 38
|
||||||
|
\BOOKMARK [2][-]{subsection.4.2.5}{\376\377\000M\000Q\000T\000T\000.\000N\000E\000T}{section.4.2}% 39
|
||||||
|
\BOOKMARK [2][-]{subsection.4.2.6}{\376\377\000N\000L\000o\000g}{section.4.2}% 40
|
||||||
|
\BOOKMARK [1][-]{section.4.3}{\376\377\000F\000r\000o\000n\000t\000e\000n\000d\000\040\000t\000e\000c\000h\000n\000o\000l\000\363\000g\000i\000\341\000k}{chapter.4}% 41
|
||||||
|
\BOOKMARK [2][-]{subsection.4.3.1}{\376\377\000R\000e\000a\000c\000t\000.\000j\000s}{section.4.3}% 42
|
||||||
|
\BOOKMARK [2][-]{subsection.4.3.2}{\376\377\000M\000a\000t\000e\000r\000i\000a\000l\000\040\000U\000I}{section.4.3}% 43
|
||||||
|
\BOOKMARK [2][-]{subsection.4.3.3}{\376\377\000A\000p\000e\000x\000c\000h\000a\000r\000t\000s}{section.4.3}% 44
|
||||||
|
\BOOKMARK [2][-]{subsection.4.3.4}{\376\377\000G\000o\000o\000g\000l\000e\000\040\000M\000a\000p\000s\000\040\000A\000p\000i}{section.4.3}% 45
|
||||||
|
\BOOKMARK [0][-]{chapter.5}{\376\377\000S\000z\000e\000r\000v\000e\000r\000\040\000o\000l\000d\000a\000l}{}% 46
|
||||||
|
\BOOKMARK [1][-]{section.5.1}{\376\377\000A\000r\000c\000h\000i\000t\000e\000k\000t\000\372\000r\000a}{chapter.5}% 47
|
||||||
|
\BOOKMARK [1][-]{section.5.2}{\376\377\000A\000d\000a\000t\000\040\000e\000l\000\351\000r\000\351\000s\000i\000\040\000r\000\351\000t\000e\000g}{chapter.5}% 48
|
||||||
|
\BOOKMARK [2][-]{subsection.5.2.1}{\376\377\000E\000n\000t\000i\000t\000\341\000s\000o\000k}{section.5.2}% 49
|
||||||
|
\BOOKMARK [2][-]{subsection.5.2.2}{\376\377\000S\000e\000e\000d\000e\000l\000\351\000s}{section.5.2}% 50
|
||||||
|
\BOOKMARK [1][-]{section.5.3}{\376\377\000\334\000z\000l\000e\000t\000i\000\040\000l\000o\000g\000i\000k\000a\000i\000\040\000r\000\351\000t\000e\000g}{chapter.5}% 51
|
BIN
docs/thesis/thesis.pdf
Normal file
106
docs/thesis/thesis.tex
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
% !TeX spellcheck = hu_HU
|
||||||
|
% !TeX encoding = UTF-8
|
||||||
|
% !TeX program = xelatex
|
||||||
|
% TODO Change language to en_GB (recommended) or en_US for English documents
|
||||||
|
\documentclass[11pt,a4paper,oneside]{report} % Single-side
|
||||||
|
%\documentclass[11pt,a4paper,twoside,openright]{report} % Duplex
|
||||||
|
|
||||||
|
\input{include/packages}
|
||||||
|
|
||||||
|
%TODO Set the main variables
|
||||||
|
\newcommand{\vikszerzoVezeteknev}{Kunkli}
|
||||||
|
\newcommand{\vikszerzoKeresztnev}{Richárd}
|
||||||
|
|
||||||
|
\newcommand{\vikkonzulensAMegszolitas}{dr.~}
|
||||||
|
\newcommand{\vikkonzulensAVezeteknev}{Simon}
|
||||||
|
\newcommand{\vikkonzulensAKeresztnev}{Csaba}
|
||||||
|
|
||||||
|
\newcommand{\vikkonzulensBMegszolitas}{}
|
||||||
|
\newcommand{\vikkonzulensBVezeteknev}{}
|
||||||
|
\newcommand{\vikkonzulensBKeresztnev}{}
|
||||||
|
|
||||||
|
\newcommand{\vikkonzulensCMegszolitas}{}
|
||||||
|
\newcommand{\vikkonzulensCVezeteknev}{}
|
||||||
|
\newcommand{\vikkonzulensCKeresztnev}{}
|
||||||
|
|
||||||
|
\newcommand{\vikcim}{Vizualizációs megoldás IoT adat elemző rendszerhez} % Cím
|
||||||
|
\newcommand{\viktanszek}{\bmetmit} % Tanszék
|
||||||
|
\newcommand{\vikdoktipus}{\bsc} % Dokumentum típusa (\bsc vagy \msc)
|
||||||
|
\newcommand{\vikmunkatipusat}{szakdolgozatot} % a "hallgató nyilatkozat" részhez: szakdolgozatot vagy diplomatervet
|
||||||
|
|
||||||
|
\input{include/tdk-variables}
|
||||||
|
\newcommand{\szerzoMeta}{\vikszerzoVezeteknev{} \vikszerzoKeresztnev} % egy szerző esetén
|
||||||
|
%\newcommand{\szerzoMeta}{\vikszerzoVezeteknev{} \vikszerzoKeresztnev, \tdkszerzoB} % két szerző esetén
|
||||||
|
|
||||||
|
%TODO Language configuration -- choose one
|
||||||
|
% Beállítások magyar nyelvű dolgozathoz
|
||||||
|
\input{include/thesis-hu}
|
||||||
|
% Settings for English documents
|
||||||
|
%\input{include/thesis-en}
|
||||||
|
|
||||||
|
\input{include/preamble}
|
||||||
|
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
% Table of contents and the main text
|
||||||
|
%--------------------------------------------------------------------------------------
|
||||||
|
\begin{document}
|
||||||
|
\pagenumbering{gobble}
|
||||||
|
|
||||||
|
%TODO These includes define guidelines -- remove these
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
%\include{include/guideline}
|
||||||
|
%\include{include/project}
|
||||||
|
|
||||||
|
\selectthesislanguage
|
||||||
|
|
||||||
|
%TODO Titlepage -- choose one from below
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
\include{include/titlepage} % Szakdolgozat/Diplomaterv címlap
|
||||||
|
%\include{include/titlepage-tdk} % TDK címlap
|
||||||
|
%\include{include/titlepage-otdk} % OTDK címlap
|
||||||
|
|
||||||
|
|
||||||
|
% Table of Contents
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
\tableofcontents\vfill
|
||||||
|
|
||||||
|
|
||||||
|
% Declaration and Abstract
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
\include{include/declaration} %TODO Hallgatói nyilatkozat -- TDK és OTDK esetén törlendő!
|
||||||
|
\include{content/abstract} %TODO Összefoglaló -- TDK és OTDK esetén nem kötelező
|
||||||
|
|
||||||
|
|
||||||
|
% The main part of the thesis
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
\pagenumbering{arabic}
|
||||||
|
|
||||||
|
%TODO import your own content
|
||||||
|
\include{content/introduction}
|
||||||
|
\include{content/birdnetes-introduction}
|
||||||
|
\include{content/birdmap-introduction}
|
||||||
|
\include{content/birdmap-technologies}
|
||||||
|
\include{content/birdmap-backend}
|
||||||
|
\include{content/birdmap-frontend}
|
||||||
|
\include{content/birdmap-test}
|
||||||
|
\include{content/birdmap-kubernetes}
|
||||||
|
\include{content/summary}
|
||||||
|
|
||||||
|
% List of Figures, Tables
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
%\listoffigures\addcontentsline{toc}{chapter}{\listfigurename}
|
||||||
|
%\listoftables\addcontentsline{toc}{chapter}{\listtablename}
|
||||||
|
|
||||||
|
|
||||||
|
% Bibliography
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
\addcontentsline{toc}{chapter}{\bibname}
|
||||||
|
\bibliography{bib/mybib}
|
||||||
|
|
||||||
|
|
||||||
|
% Appendix
|
||||||
|
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
\include{content/appendices}
|
||||||
|
|
||||||
|
%\label{page:last}
|
||||||
|
\end{document}
|