Skip to content

Commit a214dc8

Browse files
committed
Add bestfish project
1 parent 252c58e commit a214dc8

File tree

4 files changed

+227
-0
lines changed

4 files changed

+227
-0
lines changed

_drafts/exploring-the-world-of-electronics.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ title: Exploring the world of electronics
33
date: 2019-11-14 03:12:39 +0300
44
categories: [Projects]
55
tags: [C++, ESP8266]
6+
img_path: assets/img/
67
---
78

89
## Story
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
---
2+
title: Aquaculture management and analysis software
3+
date: 2015-05-16 01:09:23 +0200
4+
categories: [Projects]
5+
tags: [DotNet, C#, WinForms, SQLite, InnoSetup, Delphi, Batch Scripting]
6+
mermaid: true
7+
img_path: assets/img/aquaculture-management-and-analysis-software/
8+
---
9+
10+
## Story
11+
I have done some small projects so far, as my colleague suggested me to develop a solution for the research institute in 2014. This was a great opportunity for me to build a more complex, larger and hopefully professional solution. We worked with the customer to gather requirements and understand their needs. Since it takes time to understand their business, I built a basic prototype to demonstrate the core features and get early feedback. I had to think about how to model their data, design the UI/UX for their specific tasks, and think about how to make it easy to install and use for people who are not technical and have basic computer skills.
12+
13+
In addition to trying to make the project itself more professional, I also wanted to make the project management more organized. I had to work with several people with different roles in the project, so I started using the project management tool Redmine to manage tasks, assign responsibilities, and track progress. Releases and previews were available for download from FTP server. For coding, I followed best practices such as using SVN for code versioning and Jenkins for continuous integration.
14+
15+
Due to time and budget constraints, it was decided to go with a single-user desktop application where the operator would work on the computer running this software. While it was possible to work with the same database from multiple workstations, I informed the client that it would be better to customize the solution to fully support multi-user access in the future if needed.
16+
17+
## Demo
18+
{% include embed/youtube.html id='X8vJlxaz5YM' %}
19+
20+
## Requirements
21+
- Create application to manage aquaculture farm data
22+
- Add reporting for analysis
23+
- Add document management to attach documentation and reports to records
24+
- Create an installer
25+
- Create accompanying documentation
26+
- Create end-user help system
27+
28+
## Solution
29+
Stack used:
30+
- C#/.NET WinForms
31+
- SQLite
32+
- InnoSetup
33+
- Delphi
34+
35+
The core application was built using C# and .NET WinForms as the UI framework for its rich controls and ease of development.
36+
37+
### UI/UX
38+
The client's vision of their workflow and tasks was mapped to forms, views, and navigation between them. I decided to use the Multiple Document Interface (MDI) approach to allow multiple document windows within the parent application window. Child windows could be minimized, maximized, and closed. I added a menu to display the currently open windows to quickly switch between them and organize them cascading, vertically, or horizontally like in Windows.
39+
40+
The application consists of a main menu with forms accessible through it. Each form has a toolbar with common actions such as Create New, Edit, Save, Print, etc. Context menus provide actions relevant to selected items. There are many forms with grids to display data in tables. To make it easier to read data in tables and to customize columns based on user preferences, I had to implement custom controls. These include the `SmartDataGridViewTable`, `SmartListView`, and `SmartTableLayoutPanel` controls, which allow you to
41+
- Apply alternating row colors to improve readability
42+
- Hide/show columns
43+
- Change column order, width
44+
- Store user column preferences in the Windows registry and restore them on form load
45+
46+
Forms are designed to be simple and focus on single tasks. Complex tasks have been split into multiple forms to avoid clutter. Navigation was made easy through the toolbar; for example, there was no need to navigate back to the grid form to access the previous or next record in the list. Any referenced item was accessible from the current context through double-click navigation.
47+
48+
### Data storage
49+
SQLite was used for data storage as a lightweight solution that was easy to back up and didn't require a database server. As I continued to work on the project after the initial release, I needed to implement database versioning to handle schema changes. This was done by storing the database version number in a table and checking it at application startup. If a newer version was required, an upgrade script would perform the necessary transformations using a transaction to keep the database up to date.
50+
51+
```sql
52+
PRAGMA foreign_keys = ON;
53+
54+
-- history of database updates
55+
CREATE TABLE [BF_UPDATES_HISTORY]
56+
(
57+
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
58+
[when] TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
59+
[version] VARCHAR(15) NOT NULL
60+
);
61+
62+
-- current version of database
63+
CREATE VIEW [BF_VIEW_CURRENT_VERSION] AS
64+
SELECT version
65+
FROM BF_UPDATES_HISTORY
66+
WHERE id = (SELECT MAX(id) FROM BF_UPDATES_HISTORY);
67+
68+
-- set default database's version
69+
INSERT INTO BF_UPDATES_HISTORY (version) VALUES ('1.0.0.0');
70+
```
71+
72+
I decided to store documents not in the database, but in the file system. A `Documents` table contains only metadata and a file path. The file is copied to the special folder during file import and a record is created in the database. This makes it easy to open and modify attached files while keeping the database lightweight. Files that should not be modified are copied and marked read-only in the file system, so that the user can still view them, but cannot modify them. For example, sample documents and formal templates.
73+
74+
To display the appropriate icon for the document in the list, I checked if the path to the icon file was present in the registry at the path `HKEY_CLASSES_ROOT\<extension>\DefaultIcon` and extracted the icon from that file. If not, I used the combination of `SHGetFileInfo` and `ExtractIconEx` WinAPI functions. SHGetFileInfo' returns the index of the system icon for a given file type based on its extension. ExtractIconEx' uses this index to extract the actual icon from the system icon resources.
75+
76+
![Icons for attached documents](document-icons.png)
77+
_Icons for attached documents_
78+
79+
### Reporting
80+
To generate reports from the data, I created a base abstract class `ReportSkeleton` that other report classes could inherit from. It contained common properties used by the print form to define whether the user is allowed to use filters, filter values, etc., and methods to retrieve data from the database based on those filters. Reports would inherit from this base class and override the methods to define their specific data source and fields to display.
81+
82+
Reports could be printed or exported to Word, Excel, and HTML documents. In fact, each report produces its output in HTML format and saves it with a different extension, depending on the export type. The trick is that both Word and Excel applications can open such files and display and format the data accordingly. Using the web format also makes it easy to display the print preview window.
83+
84+
### Distribution
85+
To distribute the application, InnoSetup was used to create a Windows installer. It provides a wizard-style interface to guide the user through the installation. I built a typical installer file, except for some custom logic to check if the .NET dependency is installed on the machine or not. This was a necessary step because the minimum supporting Windows version was Windows XP SP2, where .NET was not a mandatory part of the system.
86+
87+
```pascal
88+
[Code]
89+
const
90+
DotNetVersion='v4';
91+
92+
function InitializeSetup(): Boolean;
93+
begin
94+
Result := true;
95+
if (not RegKeyExists(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + DotNetVersion)) then
96+
begin
97+
MsgBox(FmtMessage(CustomMessage('DotNetIsNotInstalled'), [DotNetVersion]), mbError, MB_OK);
98+
99+
Abort();
100+
end;
101+
end;
102+
103+
[CustomMessages]
104+
DotNetIsNotInstalled='Програма Bestfish.NET не може бути встановлена, так як на даному комп''ютері не встановлений Microsoft .NET Framework %1. Вставте інсталяційний CD диск в оптичний привід вашого комп''ютера і встановіть всі необхідні доповнення. Після чого спробуйте знову'.
105+
```
106+
107+
To automate the build process, I created batch scripts to build the application, create the database, and generate the installer. This allowed me to minimize the build step logic in Jenkins and keep it up-to-date directly in the codebase.
108+
109+
```batch
110+
:: Configuration parameters
111+
set SetupOutputFolder=X:\output
112+
set sqlite3=X:\3rd_party\util\sqlite3.exe
113+
set databasesql=X:\install\db.sql
114+
set compiler=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
115+
set keys=/p:Configuration=Release
116+
set projectfile=X:\project\Bestfish.NET.csproj
117+
set setupfile=X:\install\bestfish.iss
118+
119+
:: Check is X: exists
120+
if not exist X:\makefile.bat set XERROR=1
121+
if [%XERROR%]==[1] echo ERROR! X: disk not found! Call makeX.bat to fix this.
122+
if [%XERROR%]==[1] exit /B 1
123+
124+
:: Remember 1st argument from command line
125+
set argument=%1
126+
:: Check argument
127+
if [%argument%]==[Build] goto Build
128+
if [%argument%]==[Database] goto Database
129+
if [%argument%]==[Setup] goto Clean
130+
131+
:Help
132+
:: Display help
133+
echo Script can be used with following arguments:
134+
echo * Build - just compile program;
135+
echo * Database - just create new database file;
136+
echo * Setup - compile release + database + pack installation file.
137+
echo .
138+
goto :EOF
139+
140+
:Build
141+
:: Perform debug build of project
142+
echo *** Perform operation: Build project ***
143+
:: Check is compiler exists
144+
if not exist %compiler% set COMPERROR=1
145+
if [%COMPERROR%]==[1] echo ERROR: Compiler not found!
146+
if [%COMPERROR%]==[1] exit /B 1
147+
:: Check is project file exists
148+
if not exist %projectfile% set PROJECTFILEERROR=1
149+
if [%PROJECTFILEERROR%]==[1] echo ERROR: Project file not found!
150+
if [%PROJECTFILEERROR%]==[1] exit /B 1
151+
:: Compile
152+
%compiler% %keys% %projectfile%
153+
if ERRORLEVEL==1 echo ERROR: Project was not built properly!
154+
if ERRORLEVEL==1 exit /B 1
155+
copy /V X:\project\bin\Release\*.* %SetupOutputFolder%
156+
if ERRORLEVEL==1 echo ERROR: Coping to output folder was not done properly!
157+
if ERRORLEVEL==1 exit /B 1
158+
echo *** Job done ***
159+
:: Go to next step or exit
160+
if [%argument%]==[Setup] goto Database
161+
goto :EOF
162+
163+
:Database
164+
:: Perform creation of new database
165+
:: Check is sqlite command line tool exists
166+
if not exist %sqlite3% set SQLITEERROR=1
167+
if [%SQLITEERROR%]==[1] echo ERROR: sqlite3.exe not found!
168+
if [%SQLITEERROR%]==[1] exit /B 1
169+
:: Check is .sql file exists. If OK then perform database making
170+
if not exist %databasesql% set SQLFILEERROR=1
171+
if [%SQLFILEERROR%]==[1] echo ERROR: database.sql file not found!
172+
if [%SQLFILEERROR%]==[1] exit /B 1
173+
:: Remove existing database
174+
echo *** Perform operation: Removing existing database ***
175+
del /Q %SetupOutputFolder%\db.sqlite
176+
if ERRORLEVEL==1 echo ERROR: Existing database can not be deleted!
177+
:: Perform operation
178+
echo *** Perform operation: Make new database ***
179+
%sqlite3% %SetupOutputFolder%\db.sqlite < %databasesql%
180+
if ERRORLEVEL==1 echo ERROR: Database was not created properly!
181+
if ERRORLEVEL==1 exit /B 1
182+
echo *** Job done ***
183+
:: Go to next step or exit
184+
if [%argument%]==[Setup] goto Pack
185+
goto :EOF
186+
187+
:Pack
188+
:: Perform packing of installation file
189+
echo *** Perform operation: Pack installation file ***
190+
:: Check is Inno Setup 5 exists
191+
if not exist "C:\Program Files\Inno Setup 5\ISCC.exe" set SETUPERROR=1
192+
if [%SETUPERROR%]==[1] echo ERROR: Inno Setup 5 not found!
193+
if [%SETUPERROR%]==[1] exit /B 1
194+
:: Pack
195+
"C:\Program Files\Inno Setup 5\ISCC.exe" %setupfile%
196+
if ERRORLEVEL==1 echo ERROR: Installation file was not created properly!
197+
if ERRORLEVEL==1 exit /B 1
198+
echo *** Job done ***
199+
goto :EOF
200+
201+
:Clean
202+
:: Perform cleaning of output folder
203+
echo *** Perform operation: Cleaning workspace ***
204+
del /S /Q /A-H %SetupOutputFolder%
205+
if ERRORLEVEL==1 echo ERROR: Workspace was not cleaned!
206+
if ERRORLEVEL==1 goto End
207+
echo *** Job done ***
208+
if [%argument%]==[Setup] goto Build
209+
goto :EOF
210+
```
211+
212+
In addition to the installer, I was asked to create an ISO image that could be burned to a CD that would contain all the necessary files and have an autorun feature. However, there was no guarantee that .NET would be installed on the machine where the CD would be used. So I had to create an autorun application using Delphi. So when the disk is inserted, the system reads `autorun.inf` and launches a Delphi application that helps install the necessary dependencies, read the associated documentation, and install the software I developed.
213+
214+
![Autorun application](autorun.png)
215+
_Autorun application_
216+
217+
It was interesting to figure out what system requirements should be defined for the end product. The good news is that there were requirements available from the frameworks being used to build the project that I used to define system requirements for my applications.
218+
219+
We created the help system using the Compiled HTML (CHM) format, which is a standard Windows format for creating help files. This allowed end users to easily access the help directly from within the application.
220+
221+
## Conclusion
222+
- It's not enough to build an application that might solve a user need. There is a huge overhead in maintaining, supporting, and preparing applications for deployment. You need to think about these issues from the beginning to properly plan and estimate project timelines.
223+
- Infrastructure and management software is just as important as the core application code. They help you plan, meet deliverables, and maintain the product over time.
224+
225+
## Extras
226+
{% include embed/youtube.html id='21sK0JMU9aI' %}
Loading
Loading

0 commit comments

Comments
 (0)